]> de.git.xonotic.org Git - xonotic/d0_blind_id.git/blob - d0_bignum-tommath.c
d38a260266d1ddc3c80f14dc24c131dc18b267db
[xonotic/d0_blind_id.git] / d0_bignum-tommath.c
1 /*
2  * FILE:        d0_bignum-gmp.c
3  * AUTHOR:      Rudolf Polzer - divVerent@xonotic.org
4  * 
5  * Copyright (c) 2010, Rudolf Polzer
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  * 3. Neither the name of the copyright holder nor the names of contributors
17  *    may be used to endorse or promote products derived from this software
18  *    without specific prior written permission.
19  * 
20  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTOR(S) ``AS IS'' AND
21  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTOR(S) BE LIABLE
24  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30  * SUCH DAMAGE.
31  *
32  * $Format:commit %H$
33  * $Id$
34  */
35
36 /* NOTE: this file links against libgmp (http://gmplib.org), which is under the
37  * Lesser General Public License 2.1 or later. You may have to abide to its
38  * terms too if you use this file.
39  * To alternatively link to OpenSSL, provide the option --with-openssl to
40  * ./configure.
41  */
42
43 #ifdef WIN32
44 #include <windows.h>
45 #include <wincrypt.h>
46 #endif
47
48 #include "d0_bignum.h"
49
50 #include <tommath.h>
51 #include <string.h>
52 #include <stdlib.h>
53 #include <assert.h>
54
55 struct d0_bignum_s
56 {
57         mp_int z;
58 };
59
60 static d0_bignum_t temp;
61
62 #include <stdio.h>
63
64 #ifdef WIN32
65 HCRYPTPROV hCryptProv;
66 #else
67 static FILE *randf;
68 #endif
69
70 void rand_bytes(unsigned char *buf, size_t n)
71 {
72 #ifdef WIN32
73         CryptGenRandom(hCryptProv, n, (PBYTE) buf);
74 #else
75         if(!randf)
76                 return;
77         fread(buf, 1, n, randf);
78 #endif
79 }
80
81 D0_WARN_UNUSED_RESULT D0_BOOL d0_bignum_INITIALIZE(void)
82 {
83         D0_BOOL ret = 1;
84         unsigned char buf[256];
85         d0_bignum_init(&temp);
86 #ifdef WIN32
87         {
88                 if(CryptAcquireContext(&hCryptProv, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT))
89                 {
90                 }
91                 else if(CryptAcquireContext(&hCryptProv, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT | CRYPT_NEWKEYSET))
92                 {
93                 }
94                 else
95                 {
96                         fprintf(stderr, "WARNING: could not initialize random number generator (CryptAcquireContext failed)\n");
97                         ret = 0;
98                         hCryptProv = NULL;
99                 }
100         }
101 #else
102         randf = fopen("/dev/urandom", "rb");
103         if(!randf)
104                 randf = fopen("/dev/random", "rb");
105         if(randf)
106         {
107                 setbuf(randf, NULL);
108         }
109         else
110         {
111                 fprintf(stderr, "WARNING: could not initialize random number generator (no random device found)\n");
112                 ret = 0;
113         }
114 #endif
115
116         return ret;
117 }
118
119 void d0_bignum_SHUTDOWN(void)
120 {
121         d0_bignum_clear(&temp);
122 #ifdef WIN32
123         if(hCryptProv)
124         {
125                 CryptReleaseContext(hCryptProv, 0);
126                 hCryptProv = NULL;
127         }
128 #endif
129 }
130
131 D0_BOOL d0_iobuf_write_bignum(d0_iobuf_t *buf, const d0_bignum_t *bignum)
132 {
133         static unsigned char numbuf[65536];
134         size_t count = 0;
135         numbuf[0] = (mp_iszero(&bignum->z) ? 0 : (bignum->z.sign == MP_ZPOS) ? 1 : 3);
136         if((numbuf[0] & 3) != 0) // nonzero
137         {
138                 count = mp_unsigned_bin_size(&bignum->z);
139                 if(count > sizeof(numbuf) - 1)
140                         return 0;
141                 mp_to_unsigned_bin(&bignum->z, numbuf+1);
142         }
143         return d0_iobuf_write_packet(buf, numbuf, count + 1);
144 }
145
146 d0_bignum_t *d0_iobuf_read_bignum(d0_iobuf_t *buf, d0_bignum_t *bignum)
147 {
148         static unsigned char numbuf[65536];
149         size_t count = sizeof(numbuf);
150         if(!d0_iobuf_read_packet(buf, numbuf, &count))
151                 return NULL;
152         if(count < 1)
153                 return NULL;
154         if(!bignum) bignum = d0_bignum_new(); if(!bignum) return NULL;
155         if(numbuf[0] & 3) // nonzero
156         {
157                 mp_read_unsigned_bin(&bignum->z, numbuf+1, count-1);
158                 if(numbuf[0] & 2) // negative
159                         bignum->z.sign = MP_NEG;
160         }
161         else // zero
162         {
163                 mp_zero(&bignum->z);
164         }
165         return bignum;
166 }
167
168 ssize_t d0_bignum_export_unsigned(const d0_bignum_t *bignum, void *buf, size_t bufsize)
169 {
170         unsigned long count;
171         count = mp_unsigned_bin_size(&bignum->z);
172         if(count > bufsize)
173                 return -1;
174         if(bufsize > count)
175         {
176                 // pad from left (big endian numbers!)
177                 memset(buf, 0, bufsize - count);
178                 buf += bufsize - count;
179         }
180         mp_to_unsigned_bin_n(&bignum->z, buf, &count);
181         if(bufsize > count)
182         {
183                 // REALLY BAD
184                 // mpz_sizeinbase lied to us
185                 // buffer overflow
186                 // there is no sane way whatsoever to handle this
187                 abort();
188         }
189         if(bufsize < count)
190         {
191                 // BAD
192                 // mpz_sizeinbase lied to us
193                 // move the number
194                 if(count == 0)
195                 {
196                         memset(buf, 0, count);
197                 }
198                 else
199                 {
200                         memmove(buf + count - bufsize, buf, bufsize);
201                         memset(buf, 0, count - bufsize);
202                 }
203         }
204         return bufsize;
205 }
206
207 d0_bignum_t *d0_bignum_import_unsigned(d0_bignum_t *bignum, const void *buf, size_t bufsize)
208 {
209         size_t count;
210         if(!bignum) bignum = d0_bignum_new(); if(!bignum) return NULL;
211         mp_read_unsigned_bin(&bignum->z, buf, bufsize);
212         return bignum;
213 }
214
215 d0_bignum_t *d0_bignum_new(void)
216 {
217         d0_bignum_t *b = d0_malloc(sizeof(d0_bignum_t));
218         mp_init(&b->z);
219         return b;
220 }
221
222 void d0_bignum_free(d0_bignum_t *a)
223 {
224         mp_clear(&a->z);
225         d0_free(a);
226 }
227
228 void d0_bignum_init(d0_bignum_t *b)
229 {
230         mp_init(&b->z);
231 }
232
233 void d0_bignum_clear(d0_bignum_t *a)
234 {
235         mp_clear(&a->z);
236 }
237
238 size_t d0_bignum_size(const d0_bignum_t *r)
239 {
240         return mp_count_bits(&r->z);
241 }
242
243 int d0_bignum_cmp(const d0_bignum_t *a, const d0_bignum_t *b)
244 {
245         return mp_cmp(&a->z, &b->z);
246 }
247
248 static d0_bignum_t *d0_bignum_rand_0_to_limit(d0_bignum_t *r, const d0_bignum_t *limit)
249 {
250         size_t n = d0_bignum_size(limit);
251         size_t b = (n + 7) / 8;
252         unsigned char mask = "\xFF\x7F\x3F\x1F\x0F\x07\x03\x01"[8*b - n];
253         unsigned char numbuf[65536];
254         assert(b <= sizeof(numbuf));
255         for(;;)
256         {
257                 rand_bytes(&numbuf, b);
258                 numbuf[0] &= mask;
259                 r = d0_bignum_import_unsigned(r, numbuf, b);
260                 if(d0_bignum_cmp(r, limit) < 0)
261                         return r;
262         }
263 }
264
265 d0_bignum_t *d0_bignum_rand_range(d0_bignum_t *r, const d0_bignum_t *min, const d0_bignum_t *max)
266 {
267         mp_sub(&max->z, &min->z, &temp.z);
268         r = d0_bignum_rand_0_to_limit(r, &temp);
269         mp_add(&r->z, &min->z, &r->z);
270         return r;
271 }
272
273 d0_bignum_t *d0_bignum_rand_bit_atmost(d0_bignum_t *r, size_t n)
274 {
275         d0_bignum_one(&temp);
276         d0_bignum_shl(&temp, &temp, n);
277         r = d0_bignum_rand_0_to_limit(r, &temp);
278         return r;
279 }
280
281 d0_bignum_t *d0_bignum_rand_bit_exact(d0_bignum_t *r, size_t n)
282 {
283         d0_bignum_one(&temp);
284         d0_bignum_shl(&temp, &temp, n-1);
285         r = d0_bignum_rand_0_to_limit(r, &temp);
286         d0_bignum_add(r, r, &temp);
287         return r;
288 }
289
290 d0_bignum_t *d0_bignum_zero(d0_bignum_t *r)
291 {
292         if(!r) r = d0_bignum_new(); if(!r) return NULL;
293         mp_zero(&r->z);
294         return r;
295 }
296
297 d0_bignum_t *d0_bignum_one(d0_bignum_t *r)
298 {
299         return d0_bignum_int(r, 1);
300 }
301
302 d0_bignum_t *d0_bignum_int(d0_bignum_t *r, int n)
303 {
304         if(!r) r = d0_bignum_new(); if(!r) return NULL;
305         mp_set_int(&r->z, n);
306         return r;
307 }
308
309 d0_bignum_t *d0_bignum_mov(d0_bignum_t *r, const d0_bignum_t *a)
310 {
311         if(r == a)
312                 return r; // trivial
313         if(!r) r = d0_bignum_new(); if(!r) return NULL;
314         mp_copy(&r->z, &a->z);
315         return r;
316 }
317
318 d0_bignum_t *d0_bignum_neg(d0_bignum_t *r, const d0_bignum_t *a)
319 {
320         if(!r) r = d0_bignum_new(); if(!r) return NULL;
321         mp_neg(&a->z, &r->z);
322         return r;
323 }
324
325 d0_bignum_t *d0_bignum_shl(d0_bignum_t *r, const d0_bignum_t *a, ssize_t n)
326 {
327         r = d0_bignum_mov(r, a);
328         if(n > 0)
329                 mp_mul_2d(&r->z,  n, &r->z);
330         else if(n < 0)
331                 mp_div_2d(&r->z, -n, &r->z, NULL);
332         return r;
333 }
334
335 d0_bignum_t *d0_bignum_add(d0_bignum_t *r, const d0_bignum_t *a, const d0_bignum_t *b)
336 {
337         if(!r) r = d0_bignum_new(); if(!r) return NULL;
338         mp_add(&a->z, &b->z, &r->z);
339         return r;
340 }
341
342 d0_bignum_t *d0_bignum_sub(d0_bignum_t *r, const d0_bignum_t *a, const d0_bignum_t *b)
343 {
344         if(!r) r = d0_bignum_new(); if(!r) return NULL;
345         mp_sub(&a->z, &b->z, &r->z);
346         return r;
347 }
348
349 d0_bignum_t *d0_bignum_mul(d0_bignum_t *r, const d0_bignum_t *a, const d0_bignum_t *b)
350 {
351         if(!r) r = d0_bignum_new(); if(!r) return NULL;
352         mp_mul(&a->z, &b->z, &r->z);
353         return r;
354 }
355
356 d0_bignum_t *d0_bignum_divmod(d0_bignum_t *q, d0_bignum_t *m, const d0_bignum_t *a, const d0_bignum_t *b)
357 {
358         if(!q && !m)
359                 m = d0_bignum_new();
360         if(q)
361                 mp_div(&a->z, &b->z, &q->z, m ? &m->z : NULL);
362         else
363                 mp_mod(&a->z, &b->z, &m->z);
364         if(m)
365                 return m;
366         else
367                 return q;
368 }
369
370 d0_bignum_t *d0_bignum_mod_add(d0_bignum_t *r, const d0_bignum_t *a, const d0_bignum_t *b, const d0_bignum_t *m)
371 {
372         if(!r) r = d0_bignum_new(); if(!r) return NULL;
373         mp_addmod(&a->z, &b->z, &m->z, &r->z);
374         return r;
375 }
376
377 d0_bignum_t *d0_bignum_mod_sub(d0_bignum_t *r, const d0_bignum_t *a, const d0_bignum_t *b, const d0_bignum_t *m)
378 {
379         if(!r) r = d0_bignum_new(); if(!r) return NULL;
380         mp_submod(&a->z, &b->z, &m->z, &r->z);
381         return r;
382 }
383
384 d0_bignum_t *d0_bignum_mod_mul(d0_bignum_t *r, const d0_bignum_t *a, const d0_bignum_t *b, const d0_bignum_t *m)
385 {
386         if(!r) r = d0_bignum_new(); if(!r) return NULL;
387         mp_mulmod(&a->z, &b->z, &m->z, &r->z);
388         return r;
389 }
390
391 d0_bignum_t *d0_bignum_mod_pow(d0_bignum_t *r, const d0_bignum_t *a, const d0_bignum_t *b, const d0_bignum_t *m)
392 {
393         if(!r) r = d0_bignum_new(); if(!r) return NULL;
394         mp_exptmod(&a->z, &b->z, &m->z, &r->z);
395         return r;
396 }
397
398 D0_BOOL d0_bignum_mod_inv(d0_bignum_t *r, const d0_bignum_t *a, const d0_bignum_t *m)
399 {
400         // here, r MUST be set, as otherwise we cannot return error state!
401         return mp_invmod(&a->z, &m->z, &r->z);
402 }
403
404 int d0_bignum_isprime(d0_bignum_t *r, int param)
405 {
406         int ret = 0;
407         mp_prime_is_prime(&r->z, param, &ret);
408         return ret;
409 }
410
411 d0_bignum_t *d0_bignum_gcd(d0_bignum_t *r, d0_bignum_t *s, d0_bignum_t *t, const d0_bignum_t *a, const d0_bignum_t *b)
412 {
413         if(!r) r = d0_bignum_new(); if(!r) return NULL;
414         if(s || t)
415                 mp_exteuclid(&a->z, &b->z, s ? &s->z : NULL, t ? &t->z : NULL, &r->z);
416         else
417                 mp_gcd(&a->z, &b->z, &r->z);
418         return r;
419 }
420
421 char *d0_bignum_tostring(const d0_bignum_t *x, unsigned int base)
422 {
423         static char str[65536];
424         mp_toradix_n(&x->z, str, base, sizeof(str));
425         return str;
426 }