File Coverage

src/pdfmake_bn.c
Criterion Covered Total %
statement 0 150 0.0
branch 0 120 0.0
condition n/a
subroutine n/a
pod n/a
total 0 270 0.0


line stmt bran cond sub pod time code
1             /*
2             * pdfmake_bn.c — Minimal big-integer arithmetic for RSA.
3             *
4             * Schoolbook multiplication + shift-subtract modular reduction +
5             * square-and-multiply exponentiation. Correct and readable; slower
6             * than Montgomery but fast enough for RSA signing (one exponentiation
7             * per document, 2048-bit keys run in well under a second).
8             */
9              
10             #include "pdfmake_bn.h"
11             #include
12              
13             /*============================================================================
14             * Internal helpers
15             *==========================================================================*/
16              
17 0           static void bn_zero(pdfmake_bn_t *a) {
18 0           memset(a->w, 0, sizeof(a->w));
19 0           a->n = 0;
20 0           }
21              
22 0           static void bn_copy(pdfmake_bn_t *dst, const pdfmake_bn_t *src) {
23 0           memcpy(dst->w, src->w, sizeof(src->w));
24 0           dst->n = src->n;
25 0           }
26              
27 0           static void bn_normalize(pdfmake_bn_t *a) {
28 0 0         while (a->n > 0 && a->w[a->n - 1] == 0) a->n--;
    0          
29 0           }
30              
31             /* Compare a and b: -1 if ab. */
32 0           static int bn_cmp(const pdfmake_bn_t *a, const pdfmake_bn_t *b) {
33             size_t i;
34 0 0         if (a->n > b->n) return 1;
35 0 0         if (a->n < b->n) return -1;
36 0 0         for (i = a->n; i > 0; i--) {
37 0           uint32_t av = a->w[i - 1];
38 0           uint32_t bv = b->w[i - 1];
39 0 0         if (av > bv) return 1;
40 0 0         if (av < bv) return -1;
41             }
42 0           return 0;
43             }
44              
45             /* a -= b (requires a >= b) */
46 0           static void bn_sub(pdfmake_bn_t *a, const pdfmake_bn_t *b) {
47 0           uint64_t borrow = 0;
48 0           size_t n = (a->n > b->n) ? a->n : b->n;
49             size_t i;
50 0 0         for (i = 0; i < n; i++) {
51 0 0         uint64_t av = (i < a->n) ? a->w[i] : 0;
52 0 0         uint64_t bv = (i < b->n) ? b->w[i] : 0;
53 0           uint64_t d = av - bv - borrow;
54 0           a->w[i] = (uint32_t)d;
55 0           borrow = (d >> 32) & 1;
56             }
57 0           a->n = n;
58 0           bn_normalize(a);
59 0           }
60              
61             /* Shift a left by 1 bit, in place. Returns the bit shifted out. */
62 0           static uint32_t bn_shl1(pdfmake_bn_t *a) {
63 0           uint32_t carry = 0;
64             size_t i;
65 0 0         for (i = 0; i < a->n; i++) {
66 0           uint32_t new_carry = a->w[i] >> 31;
67 0           a->w[i] = (a->w[i] << 1) | carry;
68 0           carry = new_carry;
69             }
70 0 0         if (carry && a->n < PDFMAKE_BN_MAX_WORDS) {
    0          
71 0           a->w[a->n++] = carry;
72 0           return 0;
73             }
74 0           return carry;
75             }
76              
77             /* Schoolbook multiplication: out = a * b. Caller ensures out has room. */
78 0           static int bn_mul(pdfmake_bn_t *out,
79             const pdfmake_bn_t *a, const pdfmake_bn_t *b) {
80             size_t i;
81 0 0         if (a->n + b->n > PDFMAKE_BN_MAX_WORDS) return -1;
82 0           bn_zero(out);
83 0 0         for (i = 0; i < a->n; i++) {
84 0           uint64_t carry = 0;
85 0           uint64_t av = a->w[i];
86             size_t j;
87             size_t k;
88 0 0         for (j = 0; j < b->n; j++) {
89 0           uint64_t cur = (uint64_t)out->w[i + j]
90 0           + av * (uint64_t)b->w[j]
91             + carry;
92 0           out->w[i + j] = (uint32_t)cur;
93 0           carry = cur >> 32;
94             }
95             /* Propagate remaining carry. */
96 0           k = i + b->n;
97 0 0         while (carry) {
98             uint64_t cur;
99 0 0         if (k >= PDFMAKE_BN_MAX_WORDS) return -1;
100 0           cur = (uint64_t)out->w[k] + carry;
101 0           out->w[k] = (uint32_t)cur;
102 0           carry = cur >> 32;
103 0           k++;
104             }
105             }
106 0           out->n = a->n + b->n;
107 0           bn_normalize(out);
108 0           return 0;
109             }
110              
111             /* r = a mod m, using shift-subtract binary long division.
112             * Requires a >= 0, m > 0. */
113 0           static void bn_mod_inplace(pdfmake_bn_t *a, const pdfmake_bn_t *m) {
114             size_t top_word;
115             uint32_t top;
116             int top_bit;
117             size_t a_bits;
118             size_t m_top_word;
119             uint32_t m_top;
120             int m_top_bit;
121             size_t m_bits;
122             pdfmake_bn_t shifted;
123             size_t shift;
124             size_t i;
125 0 0         if (bn_cmp(a, m) < 0) return;
126              
127             /* Compute the highest set bit of `a`. */
128 0 0         if (a->n == 0) return;
129 0           top_word = a->n - 1;
130 0           top = a->w[top_word];
131 0           top_bit = 31;
132 0 0         while ((top & ((uint32_t)1 << top_bit)) == 0) top_bit--;
133 0           a_bits = top_word * 32 + (size_t)top_bit + 1;
134              
135             /* m bit length. */
136 0 0         if (m->n == 0) return;
137 0           m_top_word = m->n - 1;
138 0           m_top = m->w[m_top_word];
139 0           m_top_bit = 31;
140 0 0         while ((m_top & ((uint32_t)1 << m_top_bit)) == 0) m_top_bit--;
141 0           m_bits = m_top_word * 32 + (size_t)m_top_bit + 1;
142              
143 0 0         if (a_bits < m_bits) return; /* already < m */
144              
145             /* Shift m left so the top bits align with a's top bits, then
146             * repeatedly subtract if a >= shifted_m, then shift right. */
147 0           bn_copy(&shifted, m);
148 0           shift = a_bits - m_bits;
149 0 0         for (i = 0; i < shift; i++) bn_shl1(&shifted);
150              
151 0 0         for (i = 0; i <= shift; i++) {
152             uint32_t carry;
153             size_t j;
154 0 0         if (bn_cmp(a, &shifted) >= 0) {
155 0           bn_sub(a, &shifted);
156             }
157             /* Shift shifted right by 1 bit. */
158 0 0         if (shifted.n == 0) break;
159 0           carry = 0;
160 0 0         for (j = shifted.n; j > 0; j--) {
161 0           uint32_t new_carry = shifted.w[j - 1] & 1;
162 0           shifted.w[j - 1] = (shifted.w[j - 1] >> 1) | (carry << 31);
163 0           carry = new_carry;
164             }
165 0           bn_normalize(&shifted);
166             }
167             }
168              
169             /*============================================================================
170             * Public API
171             *==========================================================================*/
172              
173 0           int pdfmake_bn_from_bytes(pdfmake_bn_t *a, const uint8_t *bytes, size_t len) {
174             size_t nw;
175             size_t i;
176 0 0         if (!a || (!bytes && len > 0)) return -1;
    0          
    0          
177 0           bn_zero(a);
178              
179             /* Skip leading zero bytes to minimize work. */
180 0 0         while (len > 0 && bytes[0] == 0) { bytes++; len--; }
    0          
181 0 0         if (len == 0) return 0;
182              
183 0           nw = (len + 3) / 4;
184 0 0         if (nw > PDFMAKE_BN_MAX_WORDS) return -1;
185              
186             /* bytes are big-endian; words[0] is LSB. */
187 0 0         for (i = 0; i < len; i++) {
188 0           size_t byte_from_end = len - 1 - i;
189 0           size_t word = byte_from_end / 4;
190 0           size_t shift = (byte_from_end % 4) * 8;
191 0           a->w[word] |= (uint32_t)bytes[i] << shift;
192             }
193 0           a->n = nw;
194 0           bn_normalize(a);
195 0           return 0;
196             }
197              
198 0           int pdfmake_bn_to_bytes(const pdfmake_bn_t *a, uint8_t *bytes, size_t out_len) {
199             size_t need;
200             uint32_t top;
201             size_t i;
202 0 0         if (!a || !bytes) return -1;
    0          
203 0           memset(bytes, 0, out_len);
204              
205             /* Check the number fits. */
206 0 0         if (a->n == 0) return 0;
207 0           need = a->n * 4;
208             /* Trim leading zero bytes. */
209 0           top = a->w[a->n - 1];
210 0 0         if ((top >> 24) == 0) need--;
211 0 0         if ((top >> 16) == 0 && need > 0) need--;
    0          
212 0 0         if ((top >> 8) == 0 && need > 0) need--;
    0          
213 0 0         if (need > out_len) return -1;
214              
215 0 0         for (i = 0; i < a->n; i++) {
216 0           uint32_t v = a->w[i];
217             int b;
218 0 0         for (b = 0; b < 4; b++) {
219 0           size_t byte_from_end = i * 4 + (size_t)b;
220 0 0         if (byte_from_end >= out_len) continue;
221 0           bytes[out_len - 1 - byte_from_end] = (uint8_t)(v >> (b * 8));
222             }
223             }
224 0           return 0;
225             }
226              
227 0           int pdfmake_bn_mod_exp(pdfmake_bn_t *r,
228             const pdfmake_bn_t *base,
229             const pdfmake_bn_t *exp,
230             const pdfmake_bn_t *mod) {
231             pdfmake_bn_t b;
232             pdfmake_bn_t result;
233 0 0         if (!r || !base || !exp || !mod) return -1;
    0          
    0          
    0          
234 0 0         if (mod->n == 0) return -1;
235              
236             /* Reduce base mod m up front. */
237 0           bn_copy(&b, base);
238 0           bn_mod_inplace(&b, mod);
239              
240             /* result = 1 */
241 0           bn_zero(&result);
242 0           result.w[0] = 1;
243 0           result.n = 1;
244              
245             /* Square-and-multiply from MSB to LSB. */
246 0 0         if (exp->n > 0) {
247 0           size_t top_word = exp->n - 1;
248 0           uint32_t top = exp->w[top_word];
249 0           int top_bit = 31;
250             size_t e_bits;
251             size_t i;
252 0 0         while ((top & ((uint32_t)1 << top_bit)) == 0) top_bit--;
253 0           e_bits = top_word * 32 + (size_t)top_bit + 1;
254              
255 0 0         for (i = e_bits; i > 0; i--) {
256 0           size_t bi = i - 1;
257 0           size_t wi = bi / 32;
258 0           uint32_t bit = (exp->w[wi] >> (bi % 32)) & 1;
259             pdfmake_bn_t tmp;
260              
261             /* result = result^2 mod m */
262 0 0         if (bn_mul(&tmp, &result, &result) != 0) return -1;
263 0           bn_mod_inplace(&tmp, mod);
264 0           bn_copy(&result, &tmp);
265              
266 0 0         if (bit) {
267             /* result = result * b mod m */
268 0 0         if (bn_mul(&tmp, &result, &b) != 0) return -1;
269 0           bn_mod_inplace(&tmp, mod);
270 0           bn_copy(&result, &tmp);
271             }
272             }
273             }
274              
275 0           bn_copy(r, &result);
276 0           return 0;
277             }