Annotation of truecrypt/crypto/aes_x86.asm, revision 1.1.1.1

1.1       root        1: 
                      2: ; ---------------------------------------------------------------------------
                      3: ; Copyright (c) 1998-2007, Brian Gladman, Worcester, UK. All rights reserved.
                      4: ; 
                      5: ; LICENSE TERMS
                      6: ; 
                      7: ; The free distribution and use of this software is allowed (with or without
                      8: ; changes) provided that:
                      9: ; 
                     10: ;  1. source code distributions include the above copyright notice, this
                     11: ;     list of conditions and the following disclaimer;
                     12: ; 
                     13: ;  2. binary distributions include the above copyright notice, this list
                     14: ;     of conditions and the following disclaimer in their documentation;
                     15: ; 
                     16: ;  3. the name of the copyright holder is not used to endorse products
                     17: ;     built using this software without specific written permission.
                     18: ; 
                     19: ; DISCLAIMER
                     20: ; 
                     21: ; This software is provided 'as is' with no explicit or implied warranties
                     22: ; in respect of its properties, including, but not limited to, correctness
                     23: ; and/or fitness for purpose.
                     24: ; ---------------------------------------------------------------------------
                     25: ; Issue 20/12/2007
                     26: ;
                     27: ; This code requires ASM_X86_V1C to be set in aesopt.h. It requires the C files
                     28: ; aeskey.c and aestab.c for support.
                     29: 
                     30: ; An AES implementation for x86 processors using the YASM (or NASM) assembler.
                     31: ; This is an assembler implementation that covers encryption and decryption
                     32: ; only and is intended as a replacement of the C file aescrypt.c. It hence
                     33: ; requires the file aeskey.c for keying and aestab.c for the AES tables. It
                     34: ; employs full tables rather than compressed tables.
                     35: 
                     36: ; This code provides the standard AES block size (128 bits, 16 bytes) and the
                     37: ; three standard AES key sizes (128, 192 and 256 bits). It has the same call
                     38: ; interface as my C implementation. The ebx, esi, edi and ebp registers are
                     39: ; preserved across calls but eax, ecx and edx and the artihmetic status flags
                     40: ; are not.  It is also important that the defines below match those used in the
                     41: ; C code.  This code uses the VC++ register saving conentions; if it is used
                     42: ; with another compiler, conventions for using and saving registers may need to
                     43: ; be checked (and calling conventions).  The YASM command line for the VC++
                     44: ; custom build step is:
                     45: ;
                     46: ;    yasm -Xvc -f win32 -o "$(TargetDir)\$(InputName).obj" "$(InputPath)"
                     47: ;
                     48: ;  The calling intefaces are:
                     49: ;
                     50: ;     AES_RETURN aes_encrypt(const unsigned char in_blk[],
                     51: ;                   unsigned char out_blk[], const aes_encrypt_ctx cx[1]);
                     52: ;
                     53: ;     AES_RETURN aes_decrypt(const unsigned char in_blk[],
                     54: ;                   unsigned char out_blk[], const aes_decrypt_ctx cx[1]);
                     55: ;
                     56: ;     AES_RETURN aes_encrypt_key<NNN>(const unsigned char key[],
                     57: ;                                            const aes_encrypt_ctx cx[1]);
                     58: ;
                     59: ;     AES_RETURN aes_decrypt_key<NNN>(const unsigned char key[],
                     60: ;                                            const aes_decrypt_ctx cx[1]);
                     61: ;
                     62: ;     AES_RETURN aes_encrypt_key(const unsigned char key[],
                     63: ;                           unsigned int len, const aes_decrypt_ctx cx[1]);
                     64: ;
                     65: ;     AES_RETURN aes_decrypt_key(const unsigned char key[],
                     66: ;                           unsigned int len, const aes_decrypt_ctx cx[1]);
                     67: ;
                     68: ; where <NNN> is 128, 102 or 256.  In the last two calls the length can be in
                     69: ; either bits or bytes.
                     70: ;
                     71: ; Comment in/out the following lines to obtain the desired subroutines. These
                     72: ; selections MUST match those in the C header file aes.h
                     73: 
                     74: ; %define AES_128                 ; define if AES with 128 bit keys is needed
                     75: ; %define AES_192                 ; define if AES with 192 bit keys is needed
                     76: %define AES_256                 ; define if AES with 256 bit keys is needed
                     77: ; %define AES_VAR                 ; define if a variable key size is needed
                     78: %define ENCRYPTION              ; define if encryption is needed
                     79: %define DECRYPTION              ; define if decryption is needed
                     80: %define AES_REV_DKS             ; define if key decryption schedule is reversed
                     81: %define LAST_ROUND_TABLES       ; define if tables are to be used for last round
                     82: 
                     83: ; offsets to parameters
                     84: 
                     85: in_blk  equ     4   ; input byte array address parameter
                     86: out_blk equ     8   ; output byte array address parameter
                     87: ctx     equ    12   ; AES context structure
                     88: stk_spc equ    20   ; stack space
                     89: %define parms  12   ; parameter space on stack
                     90: 
                     91: ; The encryption key schedule has the following in memory layout where N is the
                     92: ; number of rounds (10, 12 or 14):
                     93: ;
                     94: ; lo: | input key (round 0)  |  ; each round is four 32-bit words
                     95: ;     | encryption round 1   |
                     96: ;     | encryption round 2   |
                     97: ;     ....
                     98: ;     | encryption round N-1 |
                     99: ; hi: | encryption round N   |
                    100: ;
                    101: ; The decryption key schedule is normally set up so that it has the same
                    102: ; layout as above by actually reversing the order of the encryption key
                    103: ; schedule in memory (this happens when AES_REV_DKS is set):
                    104: ;
                    105: ; lo: | decryption round 0   | =              | encryption round N   |
                    106: ;     | decryption round 1   | = INV_MIX_COL[ | encryption round N-1 | ]
                    107: ;     | decryption round 2   | = INV_MIX_COL[ | encryption round N-2 | ]
                    108: ;     ....                       ....
                    109: ;     | decryption round N-1 | = INV_MIX_COL[ | encryption round 1   | ]
                    110: ; hi: | decryption round N   | =              | input key (round 0)  |
                    111: ;
                    112: ; with rounds except the first and last modified using inv_mix_column()
                    113: ; But if AES_REV_DKS is NOT set the order of keys is left as it is for
                    114: ; encryption so that it has to be accessed in reverse when used for
                    115: ; decryption (although the inverse mix column modifications are done)
                    116: ;
                    117: ; lo: | decryption round 0   | =              | input key (round 0)  |
                    118: ;     | decryption round 1   | = INV_MIX_COL[ | encryption round 1   | ]
                    119: ;     | decryption round 2   | = INV_MIX_COL[ | encryption round 2   | ]
                    120: ;     ....                       ....
                    121: ;     | decryption round N-1 | = INV_MIX_COL[ | encryption round N-1 | ]
                    122: ; hi: | decryption round N   | =              | encryption round N   |
                    123: ;
                    124: ; This layout is faster when the assembler key scheduling provided here
                    125: ; is used.
                    126: ;
                    127: ; The DLL interface must use the _stdcall convention in which the number
                    128: ; of bytes of parameter space is added after an @ to the sutine's name.
                    129: ; We must also remove our parameters from the stack before return (see
                    130: ; the do_exit macro). Define DLL_EXPORT for the Dynamic Link Library version.
                    131: 
                    132: ;%define DLL_EXPORT
                    133: 
                    134: ; End of user defines
                    135: 
                    136: %ifdef AES_VAR
                    137: %ifndef AES_128
                    138: %define AES_128
                    139: %endif
                    140: %ifndef AES_192
                    141: %define AES_192
                    142: %endif
                    143: %ifndef AES_256
                    144: %define AES_256
                    145: %endif
                    146: %endif
                    147: 
                    148: %ifdef AES_VAR
                    149: %define KS_LENGTH       60
                    150: %elifdef AES_256
                    151: %define KS_LENGTH       60
                    152: %elifdef AES_192
                    153: %define KS_LENGTH       52
                    154: %else
                    155: %define KS_LENGTH       44
                    156: %endif
                    157: 
                    158: ; These macros implement stack based local variables
                    159: 
                    160: %macro  save 2
                    161:     mov     [esp+4*%1],%2
                    162: %endmacro
                    163: 
                    164: %macro  restore 2
                    165:     mov     %1,[esp+4*%2]
                    166: %endmacro
                    167: 
                    168: ; the DLL has to implement the _stdcall calling interface on return
                    169: ; In this case we have to take our parameters (3 4-byte pointers)
                    170: ; off the stack
                    171: 
                    172: %macro  do_name 1-2 parms
                    173: %ifndef DLL_EXPORT
                    174:     align 32
                    175:     global  %1
                    176: %1:
                    177: %else
                    178:     align 32
                    179:     global  %1@%2
                    180:     export  %1@%2
                    181: %1@%2:
                    182: %endif
                    183: %endmacro
                    184: 
                    185: %macro  do_call 1-2 parms
                    186: %ifndef DLL_EXPORT
                    187:     call    %1
                    188:     add     esp,%2
                    189: %else
                    190:     call    %1@%2
                    191: %endif
                    192: %endmacro
                    193: 
                    194: %macro  do_exit  0-1 parms
                    195: %ifdef DLL_EXPORT
                    196:     ret %1
                    197: %else
                    198:     ret
                    199: %endif
                    200: %endmacro
                    201: 
                    202: %ifdef  ENCRYPTION
                    203: 
                    204:     extern  _t_fn
                    205: 
                    206: %define etab_0(x)   [_t_fn+4*x]
                    207: %define etab_1(x)   [_t_fn+1024+4*x]
                    208: %define etab_2(x)   [_t_fn+2048+4*x]
                    209: %define etab_3(x)   [_t_fn+3072+4*x]
                    210: 
                    211: %ifdef LAST_ROUND_TABLES
                    212: 
                    213:     extern  _t_fl
                    214: 
                    215: %define eltab_0(x)  [_t_fl+4*x]
                    216: %define eltab_1(x)  [_t_fl+1024+4*x]
                    217: %define eltab_2(x)  [_t_fl+2048+4*x]
                    218: %define eltab_3(x)  [_t_fl+3072+4*x]
                    219: 
                    220: %else
                    221: 
                    222: %define etab_b(x)   byte [_t_fn+3072+4*x]
                    223: 
                    224: %endif
                    225: 
                    226: ; ROUND FUNCTION.  Build column[2] on ESI and column[3] on EDI that have the
                    227: ; round keys pre-loaded. Build column[0] in EBP and column[1] in EBX.
                    228: ;
                    229: ; Input:
                    230: ;
                    231: ;   EAX     column[0]
                    232: ;   EBX     column[1]
                    233: ;   ECX     column[2]
                    234: ;   EDX     column[3]
                    235: ;   ESI     column key[round][2]
                    236: ;   EDI     column key[round][3]
                    237: ;   EBP     scratch
                    238: ;
                    239: ; Output:
                    240: ;
                    241: ;   EBP     column[0]   unkeyed
                    242: ;   EBX     column[1]   unkeyed
                    243: ;   ESI     column[2]   keyed
                    244: ;   EDI     column[3]   keyed
                    245: ;   EAX     scratch
                    246: ;   ECX     scratch
                    247: ;   EDX     scratch
                    248: 
                    249: %macro rnd_fun 2
                    250: 
                    251:     rol     ebx,16
                    252:     %1      esi, cl, 0, ebp
                    253:     %1      esi, dh, 1, ebp
                    254:     %1      esi, bh, 3, ebp
                    255:     %1      edi, dl, 0, ebp
                    256:     %1      edi, ah, 1, ebp
                    257:     %1      edi, bl, 2, ebp
                    258:     %2      ebp, al, 0, ebp
                    259:     shr     ebx,16
                    260:     and     eax,0xffff0000
                    261:     or      eax,ebx
                    262:     shr     edx,16
                    263:     %1      ebp, ah, 1, ebx
                    264:     %1      ebp, dh, 3, ebx
                    265:     %2      ebx, dl, 2, ebx
                    266:     %1      ebx, ch, 1, edx
                    267:     %1      ebx, al, 0, edx
                    268:     shr     eax,16
                    269:     shr     ecx,16
                    270:     %1      ebp, cl, 2, edx
                    271:     %1      edi, ch, 3, edx
                    272:     %1      esi, al, 2, edx
                    273:     %1      ebx, ah, 3, edx
                    274: 
                    275: %endmacro
                    276: 
                    277: ; Basic MOV and XOR Operations for normal rounds
                    278: 
                    279: %macro  nr_xor  4
                    280:     movzx   %4,%2
                    281:     xor     %1,etab_%3(%4)
                    282: %endmacro
                    283: 
                    284: %macro  nr_mov  4
                    285:     movzx   %4,%2
                    286:     mov     %1,etab_%3(%4)
                    287: %endmacro
                    288: 
                    289: ; Basic MOV and XOR Operations for last round
                    290: 
                    291: %ifdef LAST_ROUND_TABLES
                    292: 
                    293:     %macro  lr_xor  4
                    294:         movzx   %4,%2
                    295:         xor     %1,eltab_%3(%4)
                    296:     %endmacro
                    297: 
                    298:     %macro  lr_mov  4
                    299:         movzx   %4,%2
                    300:         mov     %1,eltab_%3(%4)
                    301:     %endmacro
                    302: 
                    303: %else
                    304: 
                    305:     %macro  lr_xor  4
                    306:         movzx   %4,%2
                    307:         movzx   %4,etab_b(%4)
                    308:     %if %3 != 0
                    309:         shl     %4,8*%3
                    310:     %endif
                    311:         xor     %1,%4
                    312:     %endmacro
                    313: 
                    314:     %macro  lr_mov  4
                    315:         movzx   %4,%2
                    316:         movzx   %1,etab_b(%4)
                    317:     %if %3 != 0
                    318:         shl     %1,8*%3
                    319:     %endif
                    320:     %endmacro
                    321: 
                    322: %endif
                    323: 
                    324: %macro enc_round 0
                    325: 
                    326:     add     ebp,16
                    327:     save    0,ebp
                    328:     mov     esi,[ebp+8]
                    329:     mov     edi,[ebp+12]
                    330: 
                    331:     rnd_fun nr_xor, nr_mov
                    332: 
                    333:     mov     eax,ebp
                    334:     mov     ecx,esi
                    335:     mov     edx,edi
                    336:     restore ebp,0
                    337:     xor     eax,[ebp]
                    338:     xor     ebx,[ebp+4]
                    339: 
                    340: %endmacro
                    341: 
                    342: %macro enc_last_round 0
                    343: 
                    344:     add     ebp,16
                    345:     save    0,ebp
                    346:     mov     esi,[ebp+8]
                    347:     mov     edi,[ebp+12]
                    348: 
                    349:     rnd_fun lr_xor, lr_mov
                    350: 
                    351:     mov     eax,ebp
                    352:     restore ebp,0
                    353:     xor     eax,[ebp]
                    354:     xor     ebx,[ebp+4]
                    355: 
                    356: %endmacro
                    357: 
                    358:     section .text align=32
                    359: 
                    360: ; AES Encryption Subroutine
                    361: 
                    362:     do_name _aes_encrypt
                    363: 
                    364:     sub     esp,stk_spc
                    365:     mov     [esp+16],ebp
                    366:     mov     [esp+12],ebx
                    367:     mov     [esp+ 8],esi
                    368:     mov     [esp+ 4],edi
                    369: 
                    370:     mov     esi,[esp+in_blk+stk_spc] ; input pointer
                    371:     mov     eax,[esi   ]
                    372:     mov     ebx,[esi+ 4]
                    373:     mov     ecx,[esi+ 8]
                    374:     mov     edx,[esi+12]
                    375: 
                    376:     mov     ebp,[esp+ctx+stk_spc]    ; key pointer
                    377:     movzx   edi,byte [ebp+4*KS_LENGTH]
                    378:     xor     eax,[ebp   ]
                    379:     xor     ebx,[ebp+ 4]
                    380:     xor     ecx,[ebp+ 8]
                    381:     xor     edx,[ebp+12]
                    382: 
                    383: ; determine the number of rounds
                    384: 
                    385:     cmp     edi,10*16
                    386:     je      .3
                    387:     cmp     edi,12*16
                    388:     je      .2
                    389:     cmp     edi,14*16
                    390:     je      .1
                    391:     mov     eax,-1
                    392:     jmp     .5
                    393: 
                    394: .1: enc_round
                    395:     enc_round
                    396: .2: enc_round
                    397:     enc_round
                    398: .3: enc_round
                    399:     enc_round
                    400:     enc_round
                    401:     enc_round
                    402:     enc_round
                    403:     enc_round
                    404:     enc_round
                    405:     enc_round
                    406:     enc_round
                    407:     enc_last_round
                    408: 
                    409:     mov     edx,[esp+out_blk+stk_spc]
                    410:     mov     [edx],eax
                    411:     mov     [edx+4],ebx
                    412:     mov     [edx+8],esi
                    413:     mov     [edx+12],edi
                    414:     xor     eax,eax
                    415: 
                    416: .5: mov     ebp,[esp+16]
                    417:     mov     ebx,[esp+12]
                    418:     mov     esi,[esp+ 8]
                    419:     mov     edi,[esp+ 4]
                    420:     add     esp,stk_spc
                    421:     do_exit
                    422: 
                    423: %endif
                    424: 
                    425: %ifdef  DECRYPTION
                    426: 
                    427:     extern  _t_in
                    428: 
                    429: %define dtab_0(x)   [_t_in+4*x]
                    430: %define dtab_1(x)   [_t_in+1024+4*x]
                    431: %define dtab_2(x)   [_t_in+2048+4*x]
                    432: %define dtab_3(x)   [_t_in+3072+4*x]
                    433: 
                    434: %ifdef LAST_ROUND_TABLES
                    435: 
                    436:     extern  _t_il
                    437: 
                    438: %define dltab_0(x)  [_t_il+4*x]
                    439: %define dltab_1(x)  [_t_il+1024+4*x]
                    440: %define dltab_2(x)  [_t_il+2048+4*x]
                    441: %define dltab_3(x)  [_t_il+3072+4*x]
                    442: 
                    443: %else
                    444: 
                    445:     extern  _t_ibox
                    446: 
                    447: %define dtab_x(x)   byte [_t_ibox+x]
                    448: 
                    449: %endif
                    450: 
                    451: %macro irn_fun 2
                    452: 
                    453:     rol eax,16
                    454:     %1      esi, cl, 0, ebp
                    455:     %1      esi, bh, 1, ebp
                    456:     %1      esi, al, 2, ebp
                    457:     %1      edi, dl, 0, ebp
                    458:     %1      edi, ch, 1, ebp
                    459:     %1      edi, ah, 3, ebp
                    460:     %2      ebp, bl, 0, ebp
                    461:     shr     eax,16
                    462:     and     ebx,0xffff0000
                    463:     or      ebx,eax
                    464:     shr     ecx,16
                    465:     %1      ebp, bh, 1, eax
                    466:     %1      ebp, ch, 3, eax
                    467:     %2      eax, cl, 2, ecx
                    468:     %1      eax, bl, 0, ecx
                    469:     %1      eax, dh, 1, ecx
                    470:     shr     ebx,16
                    471:     shr     edx,16
                    472:     %1      esi, dh, 3, ecx
                    473:     %1      ebp, dl, 2, ecx
                    474:     %1      eax, bh, 3, ecx
                    475:     %1      edi, bl, 2, ecx
                    476: 
                    477: %endmacro
                    478: 
                    479: ; Basic MOV and XOR Operations for normal rounds
                    480: 
                    481: %macro  ni_xor  4
                    482:     movzx   %4,%2
                    483:     xor     %1,dtab_%3(%4)
                    484: %endmacro
                    485: 
                    486: %macro  ni_mov  4
                    487:     movzx   %4,%2
                    488:     mov     %1,dtab_%3(%4)
                    489: %endmacro
                    490: 
                    491: ; Basic MOV and XOR Operations for last round
                    492: 
                    493: %ifdef LAST_ROUND_TABLES
                    494: 
                    495: %macro  li_xor  4
                    496:     movzx   %4,%2
                    497:     xor     %1,dltab_%3(%4)
                    498: %endmacro
                    499: 
                    500: %macro  li_mov  4
                    501:     movzx   %4,%2
                    502:     mov     %1,dltab_%3(%4)
                    503: %endmacro
                    504: 
                    505: %else
                    506: 
                    507:     %macro  li_xor  4
                    508:         movzx   %4,%2
                    509:         movzx   %4,dtab_x(%4)
                    510:     %if %3 != 0
                    511:         shl     %4,8*%3
                    512:     %endif
                    513:         xor     %1,%4
                    514:     %endmacro
                    515: 
                    516:     %macro  li_mov  4
                    517:         movzx   %4,%2
                    518:         movzx   %1,dtab_x(%4)
                    519:     %if %3 != 0
                    520:         shl     %1,8*%3
                    521:     %endif
                    522:     %endmacro
                    523: 
                    524: %endif
                    525: 
                    526: %macro dec_round 0
                    527: 
                    528: %ifdef AES_REV_DKS
                    529:     add     ebp,16
                    530: %else
                    531:     sub     ebp,16
                    532: %endif
                    533:     save    0,ebp
                    534:     mov     esi,[ebp+8]
                    535:     mov     edi,[ebp+12]
                    536: 
                    537:     irn_fun ni_xor, ni_mov
                    538: 
                    539:     mov     ebx,ebp
                    540:     mov     ecx,esi
                    541:     mov     edx,edi
                    542:     restore ebp,0
                    543:     xor     eax,[ebp]
                    544:     xor     ebx,[ebp+4]
                    545: 
                    546: %endmacro
                    547: 
                    548: %macro dec_last_round 0
                    549: 
                    550: %ifdef AES_REV_DKS
                    551:     add     ebp,16
                    552: %else
                    553:     sub     ebp,16
                    554: %endif
                    555:     save    0,ebp
                    556:     mov     esi,[ebp+8]
                    557:     mov     edi,[ebp+12]
                    558: 
                    559:     irn_fun li_xor, li_mov
                    560: 
                    561:     mov     ebx,ebp
                    562:     restore ebp,0
                    563:     xor     eax,[ebp]
                    564:     xor     ebx,[ebp+4]
                    565: 
                    566: %endmacro
                    567: 
                    568:     section .text
                    569: 
                    570: ; AES Decryption Subroutine
                    571: 
                    572:     do_name _aes_decrypt
                    573: 
                    574:     sub     esp,stk_spc
                    575:     mov     [esp+16],ebp
                    576:     mov     [esp+12],ebx
                    577:     mov     [esp+ 8],esi
                    578:     mov     [esp+ 4],edi
                    579: 
                    580: ; input four columns and xor in first round key
                    581: 
                    582:     mov     esi,[esp+in_blk+stk_spc] ; input pointer
                    583:     mov     eax,[esi   ]
                    584:     mov     ebx,[esi+ 4]
                    585:     mov     ecx,[esi+ 8]
                    586:     mov     edx,[esi+12]
                    587:     lea     esi,[esi+16]
                    588: 
                    589:     mov     ebp,[esp+ctx+stk_spc]    ; key pointer
                    590:     movzx   edi,byte[ebp+4*KS_LENGTH]
                    591: %ifndef  AES_REV_DKS        ; if decryption key schedule is not reversed
                    592:     lea     ebp,[ebp+edi]   ; we have to access it from the top down
                    593: %endif
                    594:     xor     eax,[ebp   ]    ; key schedule
                    595:     xor     ebx,[ebp+ 4]
                    596:     xor     ecx,[ebp+ 8]
                    597:     xor     edx,[ebp+12]
                    598: 
                    599: ; determine the number of rounds
                    600: 
                    601:     cmp     edi,10*16
                    602:     je      .3
                    603:     cmp     edi,12*16
                    604:     je      .2
                    605:     cmp     edi,14*16
                    606:     je      .1
                    607:     mov     eax,-1
                    608:     jmp     .5
                    609: 
                    610: .1: dec_round
                    611:     dec_round
                    612: .2: dec_round
                    613:     dec_round
                    614: .3: dec_round
                    615:     dec_round
                    616:     dec_round
                    617:     dec_round
                    618:     dec_round
                    619:     dec_round
                    620:     dec_round
                    621:     dec_round
                    622:     dec_round
                    623:     dec_last_round
                    624: 
                    625: ; move final values to the output array.
                    626: 
                    627:     mov     ebp,[esp+out_blk+stk_spc]
                    628:     mov     [ebp],eax
                    629:     mov     [ebp+4],ebx
                    630:     mov     [ebp+8],esi
                    631:     mov     [ebp+12],edi
                    632:     xor     eax,eax
                    633: 
                    634: .5: mov     ebp,[esp+16]
                    635:     mov     ebx,[esp+12]
                    636:     mov     esi,[esp+ 8]
                    637:     mov     edi,[esp+ 4]
                    638:     add     esp,stk_spc
                    639:     do_exit
                    640: 
                    641: %endif
                    642: 
                    643:     end
                    644: 

unix.superglobalmegacorp.com

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