/* Bignum routines adapted from PUTTY sources. PuTTY copyright notice follows. * * PuTTY is copyright 1997-2007 Simon Tatham. * * Portions copyright Robert de Bath, Joris van Rantwijk, Delian * Delchev, Andreas Schultz, Jeroen Massar, Wez Furlong, Nicolas Barry, * Justin Bradford, Ben Harris, Malcolm Smith, Ahmad Khalifa, Markus * Kuhn, and CORE SDI S.A. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation files * (the "Software"), to deal in the Software without restriction, * including without limitation the rights to use, copy, modify, merge, * publish, distribute, sublicense, and/or sell copies of the Software, * and to permit persons to whom the Software is furnished to do so, * subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE * FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * */ #ifdef DWC_CRYPTOLIB #ifndef CONFIG_MACH_IPMATE #include "dwc_modpow.h" #define BIGNUM_INT_MASK 0xFFFFFFFFUL #define BIGNUM_TOP_BIT 0x80000000UL #define BIGNUM_INT_BITS 32 static void *snmalloc(void *mem_ctx, size_t n, size_t size) { void *p; size *= n; if (size == 0) size = 1; p = dwc_alloc(mem_ctx, size); return p; } #define snewn(ctx, n, type) ((type *)snmalloc((ctx), (n), sizeof(type))) #define sfree dwc_free /* * Usage notes: * * Do not call the DIVMOD_WORD macro with expressions such as array * subscripts, as some implementations object to this (see below). * * Note that none of the division methods below will cope if the * quotient won't fit into BIGNUM_INT_BITS. Callers should be careful * to avoid this case. * If this condition occurs, in the case of the x86 DIV instruction, * an overflow exception will occur, which (according to a correspondent) * will manifest on Windows as something like * 0xC0000095: Integer overflow * The C variant won't give the right answer, either. */ #define MUL_WORD(w1, w2) ((BignumDblInt)w1 * w2) #if defined __GNUC__ && defined __i386__ #define DIVMOD_WORD(q, r, hi, lo, w) \ __asm__("div %2" : \ "=d" (r), "=a" (q) : \ "r" (w), "d" (hi), "a" (lo)) #else #define DIVMOD_WORD(q, r, hi, lo, w) do { \ BignumDblInt n = (((BignumDblInt)hi) << BIGNUM_INT_BITS) | lo; \ q = n / w; \ r = n % w; \ } while (0) #endif // q = n / w; // r = n % w; #define BIGNUM_INT_BYTES (BIGNUM_INT_BITS / 8) #define BIGNUM_INTERNAL static Bignum newbn(void *mem_ctx, int length) { Bignum b = snewn(mem_ctx, length + 1, BignumInt); //if (!b) //abort(); /* FIXME */ DWC_MEMSET(b, 0, (length + 1) * sizeof(*b)); b[0] = length; return b; } void freebn(void *mem_ctx, Bignum b) { /* * Burn the evidence, just in case. */ DWC_MEMSET(b, 0, sizeof(b[0]) * (b[0] + 1)); sfree(mem_ctx, b); } /* * Compute c = a * b. * Input is in the first len words of a and b. * Result is returned in the first 2*len words of c. */ static void internal_mul(BignumInt *a, BignumInt *b, BignumInt *c, int len) { int i, j; BignumDblInt t; for (j = 0; j < 2 * len; j++) c[j] = 0; for (i = len - 1; i >= 0; i--) { t = 0; for (j = len - 1; j >= 0; j--) { t += MUL_WORD(a[i], (BignumDblInt) b[j]); t += (BignumDblInt) c[i + j + 1]; c[i + j + 1] = (BignumInt) t; t = t >> BIGNUM_INT_BITS; } c[i] = (BignumInt) t; } } static void internal_add_shifted(BignumInt *number, unsigned n, int shift) { int word = 1 + (shift / BIGNUM_INT_BITS); int bshift = shift % BIGNUM_INT_BITS; BignumDblInt addend; addend = (BignumDblInt)n << bshift; while (addend) { addend += number[word]; number[word] = (BignumInt) addend & BIGNUM_INT_MASK; addend >>= BIGNUM_INT_BITS; word++; } } /* * Compute a = a % m. * Input in first alen words of a and first mlen words of m. * Output in first alen words of a * (of which first alen-mlen words will be zero). * The MSW of m MUST have its high bit set. * Quotient is accumulated in the `quotient' array, which is a Bignum * rather than the internal bigendian format. Quotient parts are shifted * left by `qshift' before adding into quot. */ static void internal_mod(BignumInt *a, int alen, BignumInt *m, int mlen, BignumInt *quot, int qshift) { BignumInt m0, m1; unsigned int h; int i, k; m0 = m[0]; if (mlen > 1) m1 = m[1]; else m1 = 0; for (i = 0; i <= alen - mlen; i++) { BignumDblInt t; unsigned int q, r, c, ai1; if (i == 0) { h = 0; } else { h = a[i - 1]; a[i - 1] = 0; } if (i == alen - 1) ai1 = 0; else ai1 = a[i + 1]; /* Find q = h:a[i] / m0 */ if (h >= m0) { /* * Special case. * * To illustrate it, suppose a BignumInt is 8 bits, and * we are dividing (say) A1:23:45:67 by A1:B2:C3. Then * our initial division will be 0xA123 / 0xA1, which * will give a quotient of 0x100 and a divide overflow. * However, the invariants in this division algorithm * are not violated, since the full number A1:23:... is * _less_ than the quotient prefix A1:B2:... and so the * following correction loop would have sorted it out. * * In this situation we set q to be the largest * quotient we _can_ stomach (0xFF, of course). */ q = BIGNUM_INT_MASK; } else { /* Macro doesn't want an array subscript expression passed * into it (see definition), so use a temporary. */ BignumInt tmplo = a[i]; DIVMOD_WORD(q, r, h, tmplo, m0); /* Refine our estimate of q by looking at h:a[i]:a[i+1] / m0:m1 */ t = MUL_WORD(m1, q); if (t > ((BignumDblInt) r << BIGNUM_INT_BITS) + ai1) { q--; t -= m1; r = (r + m0) & BIGNUM_INT_MASK; /* overflow? */ if (r >= (BignumDblInt) m0 && t > ((BignumDblInt) r << BIGNUM_INT_BITS) + ai1) q--; } } /* Subtract q * m from a[i...] */ c = 0; for (k = mlen - 1; k >= 0; k--) { t = MUL_WORD(q, m[k]); t += c; c = (unsigned)(t >> BIGNUM_INT_BITS); if ((BignumInt) t > a[i + k]) c++; a[i + k] -= (BignumInt) t; } /* Add back m in case of borrow */ if (c != h) { t = 0; for (k = mlen - 1; k >= 0; k--) { t += m[k]; t += a[i + k]; a[i + k] = (BignumInt) t; t = t >> BIGNUM_INT_BITS; } q--; } if (quot) internal_add_shifted(quot, q, qshift + BIGNUM_INT_BITS * (alen - mlen - i)); } } /* * Compute p % mod. * The most significant word of mod MUST be non-zero. * We assume that the result array is the same size as the mod array. * We optionally write out a quotient if `quotient' is non-NULL. * We can avoid writing out the result if `result' is NULL. */ void bigdivmod(void *mem_ctx, Bignum p, Bignum mod, Bignum result, Bignum quotient) { BignumInt *n, *m; int mshift; int plen, mlen, i, j; /* Allocate m of size mlen, copy mod to m */ /* We use big endian internally */ mlen = mod[0]; m = snewn(mem_ctx, mlen, BignumInt); //if (!m) //abort(); /* FIXME */ for (j = 0; j < mlen; j++) m[j] = mod[mod[0] - j]; /* Shift m left to make msb bit set */ for (mshift = 0; mshift < BIGNUM_INT_BITS-1; mshift++) if ((m[0] << mshift) & BIGNUM_TOP_BIT) break; if (mshift) { for (i = 0; i < mlen - 1; i++) m[i] = (m[i] << mshift) | (m[i + 1] >> (BIGNUM_INT_BITS - mshift)); m[mlen - 1] = m[mlen - 1] << mshift; } plen = p[0]; /* Ensure plen > mlen */ if (plen <= mlen) plen = mlen + 1; /* Allocate n of size plen, copy p to n */ n = snewn(mem_ctx, plen, BignumInt); //if (!n) //abort(); /* FIXME */ for (j = 0; j < plen; j++) n[j] = 0; for (j = 1; j <= (int)p[0]; j++) n[plen - j] = p[j]; /* Main computation */ internal_mod(n, plen, m, mlen, quotient, mshift); /* Fixup result in case the modulus was shifted */ if (mshift) { for (i = plen - mlen - 1; i < plen - 1; i++) n[i] = (n[i] << mshift) | (n[i + 1] >> (BIGNUM_INT_BITS - mshift)); n[plen - 1] = n[plen - 1] << mshift; internal_mod(n, plen, m, mlen, quotient, 0); for (i = plen - 1; i >= plen - mlen; i--) n[i] = (n[i] >> mshift) | (n[i - 1] << (BIGNUM_INT_BITS - mshift)); } /* Copy result to buffer */ if (result) { for (i = 1; i <= (int)result[0]; i++) { int j = plen - i; result[i] = j >= 0 ? n[j] : 0; } } /* Free temporary arrays */ for (i = 0; i < mlen; i++) m[i] = 0; sfree(mem_ctx, m); for (i = 0; i < plen; i++) n[i] = 0; sfree(mem_ctx, n); } /* * Simple remainder. */ Bignum bigmod(void *mem_ctx, Bignum a, Bignum b) { Bignum r = newbn(mem_ctx, b[0]); bigdivmod(mem_ctx, a, b, r, NULL); return r; } /* * Compute (base ^ exp) % mod. */ Bignum dwc_modpow(void *mem_ctx, Bignum base_in, Bignum exp, Bignum mod) { BignumInt *a, *b, *n, *m; int mshift; int mlen, i, j; Bignum base, result; /* * The most significant word of mod needs to be non-zero. It * should already be, but let's make sure. */ //assert(mod[mod[0]] != 0); /* * Make sure the base is smaller than the modulus, by reducing * it modulo the modulus if not. */ base = bigmod(mem_ctx, base_in, mod); /* Allocate m of size mlen, copy mod to m */ /* We use big endian internally */ mlen = mod[0]; m = snewn(mem_ctx, mlen, BignumInt); //if (!m) //abort(); /* FIXME */ for (j = 0; j < mlen; j++) m[j] = mod[mod[0] - j]; /* Shift m left to make msb bit set */ for (mshift = 0; mshift < BIGNUM_INT_BITS - 1; mshift++) if ((m[0] << mshift) & BIGNUM_TOP_BIT) break; if (mshift) { for (i = 0; i < mlen - 1; i++) m[i] = (m[i] << mshift) | (m[i + 1] >> (BIGNUM_INT_BITS - mshift)); m[mlen - 1] = m[mlen - 1] << mshift; } /* Allocate n of size mlen, copy base to n */ n = snewn(mem_ctx, mlen, BignumInt); //if (!n) //abort(); /* FIXME */ i = mlen - base[0]; for (j = 0; j < i; j++) n[j] = 0; for (j = 0; j < base[0]; j++) n[i + j] = base[base[0] - j]; /* Allocate a and b of size 2*mlen. Set a = 1 */ a = snewn(mem_ctx, 2 * mlen, BignumInt); //if (!a) //abort(); /* FIXME */ b = snewn(mem_ctx, 2 * mlen, BignumInt); //if (!b) //abort(); /* FIXME */ for (i = 0; i < 2 * mlen; i++) a[i] = 0; a[2 * mlen - 1] = 1; /* Skip leading zero bits of exp. */ i = 0; j = BIGNUM_INT_BITS - 1; while (i < exp[0] && (exp[exp[0] - i] & (1 << j)) == 0) { j--; if (j < 0) { i++; j = BIGNUM_INT_BITS - 1; } } /* Main computation */ while (i < exp[0]) { while (j >= 0) { internal_mul(a + mlen, a + mlen, b, mlen); internal_mod(b, mlen * 2, m, mlen, NULL, 0); if ((exp[exp[0] - i] & (1 << j)) != 0) { internal_mul(b + mlen, n, a, mlen); internal_mod(a, mlen * 2, m, mlen, NULL, 0); } else { BignumInt *t; t = a; a = b; b = t; } j--; } i++; j = BIGNUM_INT_BITS - 1; } /* Fixup result in case the modulus was shifted */ if (mshift) { for (i = mlen - 1; i < 2 * mlen - 1; i++) a[i] = (a[i] << mshift) | (a[i + 1] >> (BIGNUM_INT_BITS - mshift)); a[2 * mlen - 1] = a[2 * mlen - 1] << mshift; internal_mod(a, mlen * 2, m, mlen, NULL, 0); for (i = 2 * mlen - 1; i >= mlen; i--) a[i] = (a[i] >> mshift) | (a[i - 1] << (BIGNUM_INT_BITS - mshift)); } /* Copy result to buffer */ result = newbn(mem_ctx, mod[0]); for (i = 0; i < mlen; i++) result[result[0] - i] = a[i + mlen]; while (result[0] > 1 && result[result[0]] == 0) result[0]--; /* Free temporary arrays */ for (i = 0; i < 2 * mlen; i++) a[i] = 0; sfree(mem_ctx, a); for (i = 0; i < 2 * mlen; i++) b[i] = 0; sfree(mem_ctx, b); for (i = 0; i < mlen; i++) m[i] = 0; sfree(mem_ctx, m); for (i = 0; i < mlen; i++) n[i] = 0; sfree(mem_ctx, n); freebn(mem_ctx, base); return result; } #ifdef UNITTEST static __u32 dh_p[] = { 96, 0xFFFFFFFF, 0xFFFFFFFF, 0xA93AD2CA, 0x4B82D120, 0xE0FD108E, 0x43DB5BFC, 0x74E5AB31, 0x08E24FA0, 0xBAD946E2, 0x770988C0, 0x7A615D6C, 0xBBE11757, 0x177B200C, 0x521F2B18, 0x3EC86A64, 0xD8760273, 0xD98A0864, 0xF12FFA06, 0x1AD2EE6B, 0xCEE3D226, 0x4A25619D, 0x1E8C94E0, 0xDB0933D7, 0xABF5AE8C, 0xA6E1E4C7, 0xB3970F85, 0x5D060C7D, 0x8AEA7157, 0x58DBEF0A, 0xECFB8504, 0xDF1CBA64, 0xA85521AB, 0x04507A33, 0xAD33170D, 0x8AAAC42D, 0x15728E5A, 0x98FA0510, 0x15D22618, 0xEA956AE5, 0x3995497C, 0x95581718, 0xDE2BCBF6, 0x6F4C52C9, 0xB5C55DF0, 0xEC07A28F, 0x9B2783A2, 0x180E8603, 0xE39E772C, 0x2E36CE3B, 0x32905E46, 0xCA18217C, 0xF1746C08, 0x4ABC9804, 0x670C354E, 0x7096966D, 0x9ED52907, 0x208552BB, 0x1C62F356, 0xDCA3AD96, 0x83655D23, 0xFD24CF5F, 0x69163FA8, 0x1C55D39A, 0x98DA4836, 0xA163BF05, 0xC2007CB8, 0xECE45B3D, 0x49286651, 0x7C4B1FE6, 0xAE9F2411, 0x5A899FA5, 0xEE386BFB, 0xF406B7ED, 0x0BFF5CB6, 0xA637ED6B, 0xF44C42E9, 0x625E7EC6, 0xE485B576, 0x6D51C245, 0x4FE1356D, 0xF25F1437, 0x302B0A6D, 0xCD3A431B, 0xEF9519B3, 0x8E3404DD, 0x514A0879, 0x3B139B22, 0x020BBEA6, 0x8A67CC74, 0x29024E08, 0x80DC1CD1, 0xC4C6628B, 0x2168C234, 0xC90FDAA2, 0xFFFFFFFF, 0xFFFFFFFF, }; static __u32 dh_a[] = { 8, 0xdf367516, 0x86459caa, 0xe2d459a4, 0xd910dae0, 0x8a8b5e37, 0x67ab31c6, 0xf0b55ea9, 0x440051d6, }; static __u32 dh_b[] = { 8, 0xded92656, 0xe07a048a, 0x6fa452cd, 0x2df89d30, 0xc75f1b0f, 0x8ce3578f, 0x7980a324, 0x5daec786, }; static __u32 dh_g[] = { 1, 2, }; int main(void) { int i; __u32 *k; k = dwc_modpow(NULL, dh_g, dh_a, dh_p); printf("\n\n"); for (i=0; i> 16; printf("%04x %04x ", m, l); if (!((i + 1)%13)) printf("\n"); } printf("\n\n"); if ((k[0] == 0x60) && (k[1] == 0x28e490e5) && (k[0x60] == 0x5a0d3d4e)) { printf("PASS\n\n"); } else { printf("FAIL\n\n"); } } #endif /* UNITTEST */ #endif /* CONFIG_MACH_IPMATE */ #endif /*DWC_CRYPTOLIB */