Annotation of pgp/src/mpiio.c, revision 1.1.1.7

1.1.1.7 ! root        1: /*      mpiio.c - C source code for multiprecision integer I/O routines.
        !             2:    Implemented Nov 86 by Philip Zimmermann
        !             3:    Last revised 13 Sep 91 by PRZ
        !             4: 
        !             5:    Boulder Software Engineering
        !             6:    3021 Eleventh Street
        !             7:    Boulder, CO 80304
        !             8:    (303) 541-0140
        !             9: 
        !            10:    (c) Copyright 1986-1994 by Philip Zimmermann.  All rights reserved.
        !            11:    The author assumes no liability for damages resulting from the use
        !            12:    of this software, even if the damage results from defects in this
        !            13:    software.  No warranty is expressed or implied.
        !            14: 
        !            15:    These routines are for multiprecision arithmetic I/O functions for
        !            16:    number-theoretic cryptographic algorithms such as ElGamal,
        !            17:    Diffie-Hellman, Rabin, or factoring studies for large composite
        !            18:    numbers, as well as Rivest-Shamir-Adleman (RSA) public key
        !            19:    cryptography.
        !            20: 
        !            21:    The external data representation for RSA messages and keys that
        !            22:    some of these library routines assume is outlined in a paper by 
        !            23:    Philip Zimmermann, "A Proposed Standard Format for RSA Cryptosystems",
        !            24:    IEEE Computer, September 1986, Vol. 19 No. 9, pages 21-34.
        !            25:    Some revisions to this data format have occurred since the paper
        !            26:    was published.
        !            27:  */
1.1.1.6   root       28: 
                     29: /* #define DEBUG */
                     30: 
                     31: 
1.1.1.7 ! root       32: #ifndef EMBEDDED               /* not EMBEDDED - not compiling for
        !            33:                                   embedded target */
        !            34: #include <stdio.h>             /* for printf, etc. */
        !            35: #else                          /* EMBEDDED - compiling for embedded target */
1.1.1.6   root       36: #define NULL (VOID *)0
                     37: #endif
                     38: 
                     39: #include "mpilib.h"
                     40: #include "mpiio.h"
                     41: #include "pgp.h"
                     42: 
1.1.1.7 ! root       43: static void puthexbyte(byte b);        /* Put out byte in ASCII hex via putchar. */
1.1.1.6   root       44: static
1.1.1.7 ! root       45: void puthexw16(word16 w);      /* Put out 16-bit word in hex,
        !            46:                                   high byte first. */
1.1.1.6   root       47: static
1.1.1.7 ! root       48: void putstr(string s);         /* Put out null-terminated ASCII
        !            49:                                   string via putchar. */
1.1.1.6   root       50: 
                     51: /*----------------- Following procedures relate to I/O ------------------*/
                     52: 
                     53: /* Returns string length, just like strlen() from <string.h> */
1.1.1.7 ! root       54: int string_length(char *s)
1.1.1.6   root       55: {
1.1.1.7 ! root       56:     int i;
        !            57:     i = 0;
        !            58:     if (s != NULL)
        !            59:        while (*s++)
        !            60:            i++;
        !            61:     return (i);
        !            62: }                              /* string_length */
1.1.1.6   root       63: 
                     64: #ifdef DEBUG
                     65: /* Returns integer 0-15 if c is an ASCII hex digit, -1 otherwise. */
1.1.1.7 ! root       66: static int ctox(int c)
1.1.1.6   root       67: {
1.1.1.7 ! root       68:     if ((c >= '0') && (c <= '9'))
        !            69:        return (c - '0');
        !            70:     if ((c >= 'a') && (c <= 'f'))
        !            71:        return ((c - 'a') + 10);
        !            72:     if ((c >= 'A') && (c <= 'F'))
        !            73:        return ((c - 'A') + 10);
        !            74:     return (-1);               /* error -- not a hex digit */
        !            75: }                              /* ctox */
1.1.1.6   root       76: 
                     77: /* Converts a possibly-signed digit string into a large binary number.
                     78:    Returns assumed radix, derived from suffix 'h','o',b','.' */
1.1.1.7 ! root       79: int str2reg(unitptr reg, string digitstr)
1.1.1.6   root       80: {
1.1.1.7 ! root       81:     unit temp[MAX_UNIT_PRECISION], base[MAX_UNIT_PRECISION];
        !            82:     int c, i;
        !            83:     boolean minus = FALSE;
        !            84:     short radix;               /* base 2-16 */
        !            85: 
        !            86:     mp_init(reg, 0);
        !            87: 
        !            88:     i = string_length(digitstr);
        !            89:     if (i == 0)
        !            90:        return (10);            /* empty string, assume radix 10 */
        !            91:     c = digitstr[i - 1];       /* get last char in string */
        !            92: 
        !            93:     switch (c) {               /* classify radix select suffix character */
        !            94:     case '.':
        !            95:        radix = 10;
        !            96:        break;
        !            97:     case 'H':
        !            98:     case 'h':
        !            99:        radix = 16;
        !           100:        break;
        !           101:     case 'O':
        !           102:     case 'o':
        !           103:        radix = 8;
        !           104:        break;
        !           105:     case 'B':                  /* caution! 'b' is a hex digit! */
        !           106:     case 'b':
        !           107:        radix = 2;
        !           108:        break;
        !           109:     default:
        !           110:        radix = 10;
        !           111:        break;
        !           112:     }
        !           113: 
        !           114:     mp_init(base, radix);
        !           115:     if ((minus = (*digitstr == '-')) != 0)
        !           116:        digitstr++;
        !           117:     while ((c = *digitstr++) != 0) {
        !           118:        if (c == ',')
        !           119:            continue;           /* allow commas in number */
        !           120:        c = ctox(c);
        !           121:        if ((c < 0) || (c >= radix))
        !           122:            break;              /* scan terminated by any non-digit */
        !           123:        mp_mult(temp, reg, base);
        !           124:        mp_move(reg, temp);
        !           125:        mp_init(temp, c);
        !           126:        mp_add(reg, temp);
        !           127:     }
        !           128:     if (minus)
        !           129:        mp_neg(reg);
        !           130:     return (radix);
        !           131: }                              /* str2reg */
        !           132: 
        !           133: #endif                         /* DEBUG */
        !           134: 
        !           135: /* These I/O functions, such as putstr, puthexbyte, and puthexw16, 
        !           136:    are provided here to avoid the need to link in printf from the 
        !           137:    C I/O library.  This is handy in an embedded application.
        !           138:    For embedded applications, use a customized putchar function, 
        !           139:    separately compiled.
        !           140:  */
1.1.1.6   root      141: 
                    142: /* Put out null-terminated ASCII string via putchar. */
1.1.1.7 ! root      143: static void putstr(string s)
1.1.1.6   root      144: {
1.1.1.7 ! root      145:     while (*s)
        !           146:        putchar(*s++);
        !           147: }                              /* putstr */
1.1.1.6   root      148: 
                    149: /* Put out byte in ASCII hex via putchar. */
1.1.1.7 ! root      150: static void puthexbyte(byte b)
1.1.1.6   root      151: {
1.1.1.7 ! root      152:     static char const nibs[] = "0123456789ABCDEF";
        !           153: 
        !           154:     putchar(nibs[b >> 4]);
        !           155:     putchar(nibs[b & 0x0F]);
        !           156: }                              /* puthexbyte */
1.1.1.6   root      157: 
                    158: /* Put out 16-bit word in hex, high byte first. */
1.1.1.7 ! root      159: static void puthexw16(word16 w)
1.1.1.6   root      160: {
1.1.1.7 ! root      161:     puthexbyte((byte) (w >> 8));
        !           162:     puthexbyte((byte) (w & 0xFF));
        !           163: }                              /* puthexw16 */
1.1.1.6   root      164: 
                    165: #ifdef UNIT32
1.1.1.7 ! root      166: 
1.1.1.6   root      167: /* Puts out 32-bit word in hex, high byte first. */
1.1.1.7 ! root      168: static void puthexw32(word32 lw)
1.1.1.6   root      169: {
1.1.1.7 ! root      170:     puthexw16((word16) (lw >> 16));
        !           171:     puthexw16((word16) (lw & 0xFFFFL));
        !           172: }                              /* puthexw32 */
        !           173: 
        !           174: #endif                         /* UNIT32 */
1.1.1.6   root      175: 
                    176: 
                    177: #ifdef UNIT8
                    178: #define puthexunit(u) puthexbyte(u)
                    179: #endif
                    180: #ifdef UNIT16
                    181: #define puthexunit(u) puthexw16(u)
                    182: #endif
                    183: #ifdef UNIT32
                    184: #define puthexunit(u) puthexw32(u)
                    185: #endif
                    186: 
                    187: #ifdef DEBUG
1.1.1.7 ! root      188: int display_in_base(string s, unitptr n, short radix)
1.1.1.6   root      189: /*
                    190:  * Display n in any base, such as base 10.  Returns number of digits.
                    191:  * s is string to label the displayed register.
                    192:  * n is multiprecision integer.
                    193:  * radix is base, 2-16. 
                    194:  */
                    195: {
1.1.1.7 ! root      196:     char buf[MAX_BIT_PRECISION + (MAX_BIT_PRECISION / 8) + 2];
        !           197:     unit r[MAX_UNIT_PRECISION], quotient[MAX_UNIT_PRECISION];
        !           198:     word16 remainder;
        !           199:     char *bp = buf;
        !           200:     char minus = FALSE;
        !           201:     int places = 0;
        !           202:     int commaplaces;           /* put commas this many digits apart */
        !           203:     int i;
        !           204: 
        !           205:     /*      If string s is just an ESC char, don't print it.
        !           206:        It's just to inhibit the \n at the end of the number.
        !           207:      */
        !           208:     if ((s[0] != '\033') || (s[1] != '\0'))
        !           209:        putstr(s);
1.1.1.6   root      210: 
1.1.1.7 ! root      211:     if ((radix < 2) || (radix > 16)) {
        !           212:        putstr("****\n");       /* radix out of range -- show error */
        !           213:        return (-1);
        !           214:     }
        !           215:     commaplaces = (radix == 10 ? 3 : (radix == 16 ? 4 :
        !           216:                               (radix == 2 ? 8 : (radix == 8 ? 8 : 1))));
        !           217:     mp_move(r, n);
        !           218:     if ((radix == 10) && mp_tstminus(r)) {
        !           219:        minus = TRUE;
        !           220:        mp_neg(r);              /* make r positive */
        !           221:     }
        !           222:     *bp = '\0';
        !           223:     do {                       /* build backwards number string */
        !           224:        if (++places > 1)
        !           225:            if ((places % commaplaces) == 1)
        !           226:                *++bp = ',';    /* 000,000,000,000 */
        !           227:        remainder = mp_shortdiv(quotient, r, radix);
        !           228:        *++bp = "0123456789ABCDEF"[remainder];  /* Isn't C wonderful? */
        !           229:        mp_move(r, quotient);
        !           230:     } while (testne(r, 0));
        !           231:     if (minus)
        !           232:        *++bp = '-';
        !           233: 
        !           234:     if (commaplaces != 1)
        !           235:        while ((++places % commaplaces) != 1)
        !           236:            *++bp = ' ';        /* pad to line up commas */
        !           237: 
        !           238:     i = string_length(s);
        !           239:     while (*bp) {
        !           240:        putchar(*bp);
        !           241:        ++i;
        !           242:        if ((*bp == ',') || commaplaces == 1)
        !           243:            if (i > (72 - commaplaces)) {
        !           244:                putchar('\n');
        !           245:                i = string_length(s);
        !           246:                while (i--)
        !           247:                    putchar(' ');
        !           248:                i = string_length(s);
        !           249:            }
        !           250:        bp--;
        !           251:     }
        !           252: 
        !           253:     /* show suffix character to designate radix */
        !           254:     switch (radix) {
        !           255:     case 10:                   /* decimal */
        !           256:        putchar('.');
        !           257:        break;
        !           258:     case 16:                   /* hex */
        !           259:        putchar('h');
        !           260:        break;
        !           261:     case 8:                    /* octal */
        !           262:        putchar('o');
        !           263:        break;
        !           264:     case 2:                    /* binary */
        !           265:        putchar('b');
        !           266:        break;
        !           267:     default:                   /* nonstandard radix */
        !           268:        /* printf("(%d)",radix); */ ;
        !           269:     }
        !           270: 
        !           271:     if ((s[0] == '\033') && (s[1] == '\0'))
        !           272:        putchar(' ');           /* supress newline */
        !           273:     else
        !           274:        putchar('\n');
1.1.1.6   root      275: 
1.1.1.7 ! root      276:     fill0((byteptr) buf, sizeof(buf)); /* burn the evidence on the stack... */
        !           277:     /* Note that local stack arrays r and quotient are now 0 */
        !           278:     return (places);
        !           279: }                              /* display_in_base */
1.1.1.6   root      280: 
1.1.1.7 ! root      281: #endif                         /* DEBUG */
1.1.1.6   root      282: 
                    283: /* Display register r in hex, with prefix string s. */
1.1.1.7 ! root      284: void mp_display(string s, unitptr r)
1.1.1.6   root      285: {
1.1.1.7 ! root      286:     short precision;
        !           287:     int i, j;
        !           288:     putstr(s);
        !           289:     normalize(r, precision);   /* strip off leading zeros */
        !           290:     if (precision == 0) {
        !           291:        putstr(" 0\n");
        !           292:        return;
        !           293:     }
        !           294:     make_msbptr(r, precision);
        !           295:     i = 0;
        !           296:     while (precision--) {
        !           297:        if (!(i++ % (16 / BYTES_PER_UNIT))) {
        !           298:            if (i > 1) {
        !           299:                putchar('\n');
        !           300:                j = string_length(s);
        !           301:                while (j--)
        !           302:                    putchar(' ');
        !           303:            }
        !           304:        }
        !           305:        puthexunit(*r);
        !           306:        putchar(' ');
        !           307:        post_lowerunit(r);
        !           308:     }
        !           309:     putchar('\n');
        !           310: }                              /* mp_display */
1.1.1.6   root      311: 
                    312: /* Returns checksum of buffer. */
1.1.1.7 ! root      313: word16 checksum(register byteptr buf, register word16 count)
1.1.1.6   root      314: {
1.1.1.7 ! root      315:     word16 cs;
        !           316:     cs = 0;
        !           317:     while (count--)
        !           318:        cs += *buf++;
        !           319:     return (cs);
        !           320: }                              /* checksum */
1.1.1.6   root      321: 
                    322: /*
                    323:  * Performs the XOR necessary for RSA Cipher Block Chaining.
                    324:  * The dst buffer ought to have 1 less byte of significance than 
                    325:  * the src buffer.  Only the least significant part of the src 
                    326:  * buffer is used.  bytecount is the size of a plaintext block.
                    327:  */
1.1.1.7 ! root      328: void cbc_xor(register unitptr dst, register unitptr src, word16 bytecount)
        !           329: {
        !           330:     short nunits;              /* units of precision */
        !           331:     nunits = bytes2units(bytecount) - 1;
        !           332:     make_lsbptr(dst, global_precision);
        !           333:     while (nunits--) {
        !           334:        *dst ^= *post_higherunit(src);
        !           335:        post_higherunit(dst);
        !           336:        bytecount -= units2bytes(1);
        !           337:     }
        !           338:     /* on the last unit, don't xor the excess top byte... */
        !           339:     *dst ^= (*src & (power_of_2(bytecount << 3) - 1));
        !           340: }                              /* cbc_xor */
        !           341: 
        !           342: /* Reverses the order of bytes in an array of bytes. */
        !           343: void hiloswap(byteptr r1, short numbytes)
        !           344: {
        !           345:     byteptr r2;
        !           346:     byte b;
        !           347:     r2 = &(r1[numbytes - 1]);
        !           348:     while (r1 < r2) {
        !           349:        b = *r1;
        !           350:        *r1++ = *r2;
        !           351:        *r2-- = b;
        !           352:     }
        !           353: }                              /* hiloswap */
1.1.1.6   root      354: 
                    355: #define byteglue(lo,hi) ((((word16) hi) << 8) + (word16) lo)
                    356: 
                    357: /****  The following functions must be changed if the external byteorder
                    358:        changes for integers in PGP packet data.
                    359: ****/
                    360: 
1.1.1.7 ! root      361: /*      Fetches a 16-bit word from where byte pointer is pointing.
        !           362:    buf points to external-format byteorder array.
        !           363:  */
        !           364: word16 fetch_word16(byte * buf)
1.1.1.6   root      365: {
1.1.1.7 ! root      366:     word16 w0, w1;
1.1.1.6   root      367: /* Assume MSB external byte ordering */
1.1.1.7 ! root      368:     w1 = *buf++;
        !           369:     w0 = *buf++;
        !           370:     return (w0 + (w1 << 8));
        !           371: }                              /* fetch_word16 */
1.1.1.6   root      372: 
                    373: /*
                    374:  * Puts a 16-bit word to where byte pointer is pointing, and 
                    375:  * returns updated byte pointer.
                    376:  * buf points to external-format byteorder array.
                    377:  */
1.1.1.7 ! root      378: byte *put_word16(word16 w, byte * buf)
1.1.1.6   root      379: {
                    380: /* Assume MSB external byte ordering */
1.1.1.7 ! root      381:     buf[1] = w & 0xff;
        !           382:     w = w >> 8;
        !           383:     buf[0] = w & 0xff;
        !           384:     return (buf + 2);
        !           385: }                              /* put_word16 */
1.1.1.6   root      386: 
1.1.1.7 ! root      387: /*      Fetches a 32-bit word from where byte pointer is pointing.
        !           388:    buf points to external-format byteorder array.
        !           389:  */
        !           390: word32 fetch_word32(byte * buf)
1.1.1.6   root      391: {
1.1.1.7 ! root      392:     word32 w0, w1, w2, w3;
1.1.1.6   root      393: /* Assume MSB external byte ordering */
1.1.1.7 ! root      394:     w3 = *buf++;
        !           395:     w2 = *buf++;
        !           396:     w1 = *buf++;
        !           397:     w0 = *buf++;
        !           398:     return (w0 + (w1 << 8) + (w2 << 16) + (w3 << 24));
        !           399: }                              /* fetch_word32 */
        !           400: 
        !           401: /*      Puts a 32-bit word to where byte pointer is pointing, and 
        !           402:    returns updated byte pointer.
        !           403:    buf points to external-format byteorder array.
        !           404:  */
        !           405: byte *put_word32(word32 w, byte * buf)
1.1.1.6   root      406: {
                    407: /* Assume MSB external byte ordering */
1.1.1.7 ! root      408:     buf[3] = w & 0xff;
        !           409:     w = w >> 8;
        !           410:     buf[2] = w & 0xff;
        !           411:     w = w >> 8;
        !           412:     buf[1] = w & 0xff;
        !           413:     w = w >> 8;
        !           414:     buf[0] = w & 0xff;
        !           415:     return (buf + 4);
        !           416: }                              /* put_word32 */
1.1.1.6   root      417: 
                    418: /***   End of functions that must be changed if the external byteorder
                    419:        changes for integer fields in PGP packets.
                    420: ***/
                    421: 
                    422: /*
                    423:  * Converts a multiprecision integer from the externally-represented 
                    424:  * form of a byte array with a 16-bit bitcount in a leading length 
                    425:  * word to the internally-used representation as a unit array.
                    426:  * Converts to INTERNAL byte order.
                    427:  * The same buffer address may be used for both r and buf.
                    428:  * Returns number of units in result, or returns -1 on error.
                    429:  */
1.1.1.7 ! root      430: short mpi2reg(register unitptr r, register byteptr buf)
1.1.1.6   root      431: {
1.1.1.7 ! root      432:     byte buf2[MAX_BYTE_PRECISION];
        !           433:     word16 bitcount, bytecount, unitcount, zero_bytes, i;
1.1.1.6   root      434: 
1.1.1.7 ! root      435:     /* First, extract 16-bit bitcount prefix from first 2 bytes... */
        !           436:     bitcount = fetch_word16(buf);
        !           437:     buf += 2;
        !           438: 
        !           439:     /* Convert bitcount to bytecount and unitcount... */
        !           440:     bytecount = bits2bytes(bitcount);
        !           441:     unitcount = bytes2units(bytecount);
        !           442:     if (unitcount > global_precision) {
        !           443:        /* precision overflow during conversion. */
        !           444:        return (-1);            /* precision overflow -- error return */
        !           445:     }
        !           446:     zero_bytes = units2bytes(global_precision) - bytecount;
1.1.1.6   root      447: /* Assume MSB external byte ordering */
1.1.1.7 ! root      448:     fill0(buf2, zero_bytes);   /* fill leading zero bytes */
        !           449:     i = zero_bytes;            /* assumes MSB first */
        !           450:     while (bytecount--)
        !           451:        buf2[i++] = *buf++;
        !           452: 
        !           453:     mp_convert_order(buf2);    /* convert to INTERNAL byte order */
        !           454:     mp_move(r, (unitptr) buf2);
        !           455:     mp_burn((unitptr) buf2);   /* burn the evidence on the stack */
        !           456:     return (unitcount);                /* returns unitcount of reg */
        !           457: }                              /* mpi2reg */
1.1.1.6   root      458: 
                    459: /*
                    460:  * Converts the multiprecision integer r from the internal form of 
                    461:  * a unit array to the normalized externally-represented form of a
                    462:  * byte array with a leading 16-bit bitcount word in buf[0] and buf[1].
                    463:  * This bitcount length prefix is exact count, not rounded up.
                    464:  * Converts to EXTERNAL byte order.
                    465:  * The same buffer address may be used for both r and buf.
                    466:  * Returns the number of bytes of the result, not counting length prefix.
                    467:  */
1.1.1.7 ! root      468: short reg2mpi(register byteptr buf, register unitptr r)
1.1.1.6   root      469: {
1.1.1.7 ! root      470:     byte buf1[MAX_BYTE_PRECISION];
        !           471:     byteptr buf2;
        !           472:     short bytecount, bc;
        !           473:     word16 bitcount;
        !           474:     bitcount = countbits(r);
1.1.1.6   root      475: #ifdef DEBUG
1.1.1.7 ! root      476:     if (bitcount > MAX_BIT_PRECISION) {
        !           477:        fprintf(stderr, "reg2mpi: bitcount out of range (%d)\n", bitcount);
        !           478:        return 0;
        !           479:     }
1.1.1.6   root      480: #endif
1.1.1.7 ! root      481:     bytecount = bits2bytes(bitcount);
        !           482:     bc = bytecount;            /* save bytecount for return */
        !           483:     buf2 = buf1;
        !           484:     mp_move((unitptr) buf2, r);
        !           485:     mp_convert_order(buf2);    /* convert to EXTERNAL byteorder */
1.1.1.6   root      486: /* Assume MSB external byte ordering */
1.1.1.7 ! root      487:     buf2 += units2bytes(global_precision) - bytecount;
        !           488:     buf = put_word16(bitcount, buf);   /* store bitcount in external
        !           489:                                           byteorder */
        !           490: 
        !           491:     while (bytecount--)
        !           492:        *buf++ = *buf2++;
        !           493: 
        !           494:     mp_burn((unitptr) buf1);   /* burn the evidence on the stack */
        !           495:     return (bc);               /* returns bytecount of mpi, not counting
        !           496:                                   prefix */
        !           497: }                              /* reg2mpi */
1.1.1.6   root      498: 
                    499: 
                    500: #ifdef DEBUG
                    501: 
                    502: /* Dump buffer in hex, with string label prefix. */
1.1.1.7 ! root      503: void dumpbuf(string s, byteptr buf, int bytecount)
1.1.1.6   root      504: {
1.1.1.7 ! root      505:     putstr(s);
        !           506:     while (bytecount--) {
        !           507:        puthexbyte(*buf++);
        !           508:        putchar(' ');
        !           509:        if ((bytecount & 0x0f) == 0)
        !           510:            putchar('\n');
        !           511:     }
        !           512: }                              /* dumpbuf */
1.1.1.6   root      513: 
                    514: /*
                    515:  * Dump unit array r as a C array initializer, with string label prefix. 
                    516:  * Array is dumped in native unit order.
                    517:  */
1.1.1.7 ! root      518: void dump_unit_array(string s, unitptr r)
1.1.1.6   root      519: {
1.1.1.7 ! root      520:     int unitcount;
        !           521:     unitcount = global_precision;
        !           522:     putstr(s);
        !           523:     putstr("\n{ ");
        !           524:     while (unitcount--) {
        !           525:        putstr("0x");
        !           526:        puthexunit(*r++);
        !           527:        putchar(',');
        !           528:        if (unitcount && ((unitcount & 0x07) == 0))
        !           529:            putstr("\n  ");
        !           530:     }
        !           531:     putstr(" 0};\n");
        !           532: }                              /* dump_unit_array */
1.1.1.6   root      533: 
1.1.1.7 ! root      534: #endif                         /* ifdef DEBUG */
1.1.1.6   root      535: 
                    536: /************ end of multiprecision integer I/O library *****************/

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.