aboutsummaryrefslogtreecommitdiffstats
path: root/src/crypto/bigint.c
blob: 5b7116e281d638ba7ffc89c6f4c09a9b3aa93e0b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
/*
 * Copyright (C) 2012 Michael Brown <mbrown@fensystems.co.uk>.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 2 of the
 * License, or any later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
 * 02110-1301, USA.
 *
 * You can also choose to distribute this program under the terms of
 * the Unmodified Binary Distribution Licence (as given in the file
 * COPYING.UBDL), provided that you have satisfied its requirements.
 */

FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );

#include <stdint.h>
#include <string.h>
#include <assert.h>
#include <ipxe/profile.h>
#include <ipxe/bigint.h>

/** @file
 *
 * Big integer support
 */

/** Modular multiplication overall profiler */
static struct profiler bigint_mod_multiply_profiler __profiler =
	{ .name = "bigint_mod_multiply" };

/** Modular multiplication multiply step profiler */
static struct profiler bigint_mod_multiply_multiply_profiler __profiler =
	{ .name = "bigint_mod_multiply.multiply" };

/** Modular multiplication rescale step profiler */
static struct profiler bigint_mod_multiply_rescale_profiler __profiler =
	{ .name = "bigint_mod_multiply.rescale" };

/** Modular multiplication subtract step profiler */
static struct profiler bigint_mod_multiply_subtract_profiler __profiler =
	{ .name = "bigint_mod_multiply.subtract" };

/**
 * Conditionally swap big integers (in constant time)
 *
 * @v first0		Element 0 of big integer to be conditionally swapped
 * @v second0		Element 0 of big integer to be conditionally swapped
 * @v size		Number of elements in big integers
 * @v swap		Swap first and second big integers
 */
void bigint_swap_raw ( bigint_element_t *first0, bigint_element_t *second0,
		       unsigned int size, int swap ) {
	bigint_element_t mask;
	bigint_element_t xor;
	unsigned int i;

	/* Construct mask */
	mask = ( ( bigint_element_t ) ( ! swap ) - 1 );

	/* Conditionally swap elements */
	for ( i = 0 ; i < size ; i++ ) {
		xor = ( mask & ( first0[i] ^ second0[i] ) );
		first0[i] ^= xor;
		second0[i] ^= xor;
	}
}

/**
 * Multiply big integers
 *
 * @v multiplicand0	Element 0 of big integer to be multiplied
 * @v multiplicand_size	Number of elements in multiplicand
 * @v multiplier0	Element 0 of big integer to be multiplied
 * @v multiplier_size	Number of elements in multiplier
 * @v result0		Element 0 of big integer to hold result
 * @v carry0		Element 0 of big integer to hold temporary carry
 */
void bigint_multiply_raw ( const bigint_element_t *multiplicand0,
			   unsigned int multiplicand_size,
			   const bigint_element_t *multiplier0,
			   unsigned int multiplier_size,
			   bigint_element_t *result0,
			   bigint_element_t *carry0 ) {
	unsigned int result_size = ( multiplicand_size + multiplier_size );
	const bigint_t ( multiplicand_size ) __attribute__ (( may_alias ))
		*multiplicand = ( ( const void * ) multiplicand0 );
	const bigint_t ( multiplier_size ) __attribute__ (( may_alias ))
		*multiplier = ( ( const void * ) multiplier0 );
	bigint_t ( result_size ) __attribute__ (( may_alias ))
		*result = ( ( void * ) result0 );
	bigint_t ( result_size ) __attribute__ (( may_alias ))
		*carry = ( ( void * ) carry0 );
	bigint_element_t multiplicand_element;
	const bigint_element_t *multiplier_element;
	bigint_element_t *result_elements;
	bigint_element_t *carry_element;
	unsigned int i;
	unsigned int j;

	/* Zero result and temporary carry space */
	memset ( result, 0, sizeof ( *result ) );
	memset ( carry, 0, sizeof ( *carry ) );

	/* Multiply integers one element at a time, adding the double
	 * element directly into the result and accumulating any
	 * overall carry out from this double-element addition into
	 * the temporary carry space.
	 *
	 * We could propagate the carry immediately instead of using a
	 * temporary carry space.  However, this would cause the
	 * multiplication to run in non-constant time, which is
	 * undesirable.
	 *
	 * The carry elements can never overflow, provided that the
	 * element size is large enough to accommodate any plausible
	 * big integer.  The total number of potential carries (across
	 * all elements) is the sum of the number of elements in the
	 * multiplicand and multiplier.  With a 16-bit element size,
	 * this therefore allows for up to a 1Mbit multiplication
	 * result (e.g. a 512kbit integer multiplied by another
	 * 512kbit integer), which is around 100x higher than could be
	 * needed in practice.  With a more realistic 32-bit element
	 * size, the limit becomes a totally implausible 128Gbit
	 * multiplication result.
	 */
	for ( i = 0 ; i < multiplicand_size ; i++ ) {
		multiplicand_element = multiplicand->element[i];
		multiplier_element = &multiplier->element[0];
		result_elements = &result->element[i];
		carry_element = &carry->element[i];
		for ( j = 0 ; j < multiplier_size ; j++ ) {
			bigint_multiply_one ( multiplicand_element,
					      *(multiplier_element++),
					      result_elements++,
					      carry_element++ );
		}
	}

	/* Add the temporary carry into the result.  The least
	 * significant element of the carry represents the carry out
	 * from multiplying the least significant elements of the
	 * multiplicand and multiplier, and therefore must be added to
	 * the third-least significant element of the result (i.e. the
	 * carry needs to be shifted left by two elements before being
	 * adding to the result).
	 *
	 * The most significant two elements of the carry are
	 * guaranteed to be zero, since:
	 *
	 *     a < 2^{n}, b < 2^{m} => ab < 2^{n+m}
	 *
	 * and the overall result of the multiplication (including
	 * adding in the shifted carries) is therefore guaranteed not
	 * to overflow beyond the end of the result.
	 *
	 * We could avoid this shifting by writing the carry directly
	 * into the "correct" element during the element-by-element
	 * multiplication stage above.  However, this would add
	 * complexity to the loop since we would have to arrange for
	 * the (provably zero) most significant two carry out results
	 * to be discarded, in order to avoid writing beyond the end
	 * of the temporary carry space.
	 *
	 * Performing the logical shift is essentially free, since we
	 * simply adjust the element pointers.
	 *
	 * To avoid requiring additional checks in each architecture's
	 * implementation of bigint_add_raw(), we explicitly avoid
	 * calling bigint_add_raw() with a size of zero.
	 */
	if ( result_size > 2 ) {
		bigint_add_raw ( &carry->element[0], &result->element[2],
				 ( result_size - 2 ) );
	}
}

/**
 * Perform modular multiplication of big integers
 *
 * @v multiplicand0	Element 0 of big integer to be multiplied
 * @v multiplier0	Element 0 of big integer to be multiplied
 * @v modulus0		Element 0 of big integer modulus
 * @v result0		Element 0 of big integer to hold result
 * @v size		Number of elements in base, modulus, and result
 * @v tmp		Temporary working space
 */
void bigint_mod_multiply_raw ( const bigint_element_t *multiplicand0,
			       const bigint_element_t *multiplier0,
			       const bigint_element_t *modulus0,
			       bigint_element_t *result0,
			       unsigned int size, void *tmp ) {
	const bigint_t ( size ) __attribute__ (( may_alias )) *multiplicand =
		( ( const void * ) multiplicand0 );
	const bigint_t ( size ) __attribute__ (( may_alias )) *multiplier =
		( ( const void * ) multiplier0 );
	const bigint_t ( size ) __attribute__ (( may_alias )) *modulus =
		( ( const void * ) modulus0 );
	bigint_t ( size ) __attribute__ (( may_alias )) *result =
		( ( void * ) result0 );
	struct {
		bigint_t ( size * 2 ) result;
		union {
			bigint_t ( size * 2 ) modulus;
			bigint_t ( size * 2 ) carry;
		};
	} *temp = tmp;
	int rotation;
	int i;

	/* Start profiling */
	profile_start ( &bigint_mod_multiply_profiler );

	/* Sanity check */
	assert ( sizeof ( *temp ) == bigint_mod_multiply_tmp_len ( modulus ) );

	/* Perform multiplication */
	profile_start ( &bigint_mod_multiply_multiply_profiler );
	bigint_multiply ( multiplicand, multiplier, &temp->result,
			  &temp->carry );
	profile_stop ( &bigint_mod_multiply_multiply_profiler );

	/* Rescale modulus to match result */
	profile_start ( &bigint_mod_multiply_rescale_profiler );
	bigint_grow ( modulus, &temp->modulus );
	rotation = ( bigint_max_set_bit ( &temp->result ) -
		     bigint_max_set_bit ( &temp->modulus ) );
	for ( i = 0 ; i < rotation ; i++ )
		bigint_rol ( &temp->modulus );
	profile_stop ( &bigint_mod_multiply_rescale_profiler );

	/* Subtract multiples of modulus */
	profile_start ( &bigint_mod_multiply_subtract_profiler );
	for ( i = 0 ; i <= rotation ; i++ ) {
		if ( bigint_is_geq ( &temp->result, &temp->modulus ) )
			bigint_subtract ( &temp->modulus, &temp->result );
		bigint_ror ( &temp->modulus );
	}
	profile_stop ( &bigint_mod_multiply_subtract_profiler );

	/* Resize result */
	bigint_shrink ( &temp->result, result );

	/* Sanity check */
	assert ( bigint_is_geq ( modulus, result ) );

	/* Stop profiling */
	profile_stop ( &bigint_mod_multiply_profiler );
}

/**
 * Perform modular exponentiation of big integers
 *
 * @v base0		Element 0 of big integer base
 * @v modulus0		Element 0 of big integer modulus
 * @v exponent0		Element 0 of big integer exponent
 * @v result0		Element 0 of big integer to hold result
 * @v size		Number of elements in base, modulus, and result
 * @v exponent_size	Number of elements in exponent
 * @v tmp		Temporary working space
 */
void bigint_mod_exp_raw ( const bigint_element_t *base0,
			  const bigint_element_t *modulus0,
			  const bigint_element_t *exponent0,
			  bigint_element_t *result0,
			  unsigned int size, unsigned int exponent_size,
			  void *tmp ) {
	const bigint_t ( size ) __attribute__ (( may_alias )) *base =
		( ( const void * ) base0 );
	const bigint_t ( size ) __attribute__ (( may_alias )) *modulus =
		( ( const void * ) modulus0 );
	const bigint_t ( exponent_size ) __attribute__ (( may_alias ))
		*exponent = ( ( const void * ) exponent0 );
	bigint_t ( size ) __attribute__ (( may_alias )) *result =
		( ( void * ) result0 );
	size_t mod_multiply_len = bigint_mod_multiply_tmp_len ( modulus );
	struct {
		bigint_t ( size ) base;
		bigint_t ( exponent_size ) exponent;
		uint8_t mod_multiply[mod_multiply_len];
	} *temp = tmp;
	static const uint8_t start[1] = { 0x01 };

	memcpy ( &temp->base, base, sizeof ( temp->base ) );
	memcpy ( &temp->exponent, exponent, sizeof ( temp->exponent ) );
	bigint_init ( result, start, sizeof ( start ) );

	while ( ! bigint_is_zero ( &temp->exponent ) ) {
		if ( bigint_bit_is_set ( &temp->exponent, 0 ) ) {
			bigint_mod_multiply ( result, &temp->base, modulus,
					      result, temp->mod_multiply );
		}
		bigint_ror ( &temp->exponent );
		bigint_mod_multiply ( &temp->base, &temp->base, modulus,
				      &temp->base, temp->mod_multiply );
	}
}