karl@ddsw1.UUCP (Karl Denninger) (04/04/88)
This is a 'C' rewrite of a publically-available encryption utility we've had in the woodwork for a while. It works on a rather simple, but surprisingly effective principle. The output while encrypted has a nearly perfect random byte distribution from 1 -- 255. Without the password, which can be up to 80 characters in length (ie: a sentence; in fact this is recommended) you're in a for a *long* search. I'm all ears on methods of attacking this, it's been through some reasonably concerted attempts to break it and no one has yet succeeded. Basic operation: The cipher is a substitution cipher executed by the 'XOR' instruction, with an interesting twist. Picture a key and plaintext, as thus: Key > a b c d e f g h i j Plain> z x y c d s f g h t The cipher proceeds down the line, from left to right, XORing the bytes one by one. This would be trivially easy to break, except for one feature: A checksum is kept of the bytes processed before and after XOR; when this checksum reaches a value the entire KEY string is XORd with the checksum value; the checksum value is then normalized to reflect the remainder from the 'preset' value. This combination makes for a cipher which produces a near-random distribution. For binary files, this is probably enough. For text, though, there is a small window in the very front of the file in which it might be possible to perform pattern-analysis to produce the password. To forestall these attempts, 768 bytes of randomly-generated garbage are prepended to the file before encryption takes place, and are removed on decryption. These random bytes serve only to provide a 'seed' for the encryption function to prevent pattern analysis. In addition, three "FF"s are added during encryption at the VERY END of the file; this facilitates location of the actual file end point if the encrypted file should be transmitted via Xmodem or any other protocol which pads received data. Of course, I welcome any comments on the implementation; those of you who believe it should be trivial to break are welcome to try - a sample text file is enclosed in the 'shar' which follows, encoded with a reasonably simple password. The only hint I will give you is that it is prose, and in English. The first person (and only the first one) who can send the plaintext of this to me by email will receive $25.00 (just to make it worth your while). The file is called 'text.uue' in the SHAR, it's a uuencoded copy of the encrypted text file. This code should run on any reasonable UNIX-based system. ---- Code begins below ---- #!/bin/sh # shar: Shell Archiver # Run the following text with /bin/sh to create: # encrypt.c # text.uue sed 's/^X//' << 'SHAR_EOF' > encrypt.c X/* Encrypt: encrypt or decrypt a file. */ X/* X This code and associated documentation is Copyright 1988 MCS, Inc. X Non-profit distribution authorized. X Please send modifications, etc. to ....ihnp4!ddsw1!karl. X X NOTE: If your system uses unsigned characters, you need to change X the define below for MARKER to be '255' instead of '-1'. X X*/ X X#include <stdio.h> X#include <fcntl.h> X#include <sys/types.h> X X#define MARKER -1 /* Marker for EOF (unsigned uses 255) */ X Xchar key[80]; /* Keep the keyspace global */ X Xenchar(ch) /* Process one character */ Xchar ch; /* Current character */ X X{ X static int kptr = 0; /* Key pointer */ X static int chsum = 0; /* Key checksum */ X char outch; X int kx; X int xkey; X X chsum += ch; /* Add before encryption */ X outch = ch ^ key[kptr++]; /* Encrypt */ X chsum += outch; /* And add after */ X if (key[kptr] == 0) /* Restart if at end */ X kptr = 0; X if (chsum >= 4096) { /* If > 2^^12 - 1 */ X chsum -= 4096; /* Roll checksum... */ X kx = 0; X xkey = chsum && 255; /* And XOR key */ X while (key[kx] != 0) { X key[kx] = key[kx] ^ xkey; X if (key[kx] == 0)/* Insure we don't stop early */ X key[kx] = key[kx] ^ xkey; X kx++; X } X } X return(outch); /* Return encrypted/decrypted */ X} X Xmain() X{ X X char infile[80]; /* Path buffers */ X char outfile[80]; X char buffer[256], bufout[256]; /* Block buffers */ X char key2[80]; /* Verification space */ X int fi, fo; /* Two fd's */ X long posit, outpos, pst, oldpos; /* file offsets */ X int jcount; /* Count of skips */ X char encdec[5]; /* Operation flag */ X int ctback, blocks, remain; /* Temp storage */ X int encrypt = 0, decrypt = 0; /* Two flags */ X int outch; /* Outbound character */ X int x, y; /* Two temps */ X X fputs("Encrypt/Decrypt : ", stdout); X gets(encdec); /* See what the user wants */ X if ((*encdec == 'e') || (*encdec == 'E')) X encrypt++; X else { X if ((*encdec == 'd') || (*encdec == 'D')) X decrypt++; X else { X puts("Invalid encrypt/decrypt, exit"); X exit(1); X } X } X fputs(" Input filename : ", stdout); X gets(infile); X fputs("Output filename : ", stdout); X gets(outfile); X fputs(" Enter key : ", stdout); X gets(key); X fputs(" Validate : ", stdout); X gets(key2); X if (strcmp(key, key2) != 0) { X puts("Key validation failed."); X exit(1); X } X puts("\nStand by, working..."); X if ((fi = open(infile, O_RDONLY)) == MARKER) { X perror("Input"); X exit(1); X } X if ((fo = open(outfile, O_WRONLY|O_CREAT, 0660)) == MARKER) { X perror("Output"); X exit(1); X } X posit = lseek(fi, 0L, 2); /* Seek EOF */ X posit--; /* Last byte is 1 less */ X if (decrypt != 0) { /* Find marker if decrypt */ X puts("Searching for marker"); X oldpos = posit; X posit -= 2; X while (posit >= 0) { X lseek(fi, posit, 0); X read(fi, buffer, 3); X if ((buffer[0] == MARKER) && (buffer[1] == MARKER) && (buffer[2] == MARKER)) { X oldpos = posit - 1; X printf("Found marker at location %d\n", oldpos); X posit = 0; X } X posit--; X } X if (posit < 0) /* Adjust file pointer */ X posit = oldpos; X } X if (encrypt != 0) { /* Add scrambling on encrypt */ X posit += 768; /* Do 3 256-byte records */ X srand(time(0L)); /* Seed random generator */ X puts("Adding seed data (768 bytes)"); X for (x = 0; x < 768; x++) { X y = (char) rand(); X outch = enchar(y); /* Add 768 random bytes */ X lseek(fo, posit, 0); X write(fo, &outch, 1); X posit--; X } X } X if (encrypt != 0) X puts("Encrypting..."); X else X puts("Decrypting..."); X if (decrypt != 0) X jcount = 768; /* Strip 768 on decrypt */ X else X jcount = 0; X blocks = ((posit + 1) / 256); /* Number of full blocks */ X remain = ((posit + 1) - (blocks * 256)); /* Remainder */ X lseek(fi, (long) (blocks * 256), 0); /* Seek to start of remainder */ X read(fi, buffer, remain); /* Get remainder */ X remain--; X for (x = remain; x >= 0; x--) /* Build remainder buffer */ X bufout[x] = enchar(buffer[x]); X if (jcount == 0) { X lseek(fo, (long) (blocks * 256), 0); X write(fo, bufout, (remain + 1)); /* Write remainder */ X } else X jcount -= (remain + 1); /* Ditch it */ X blocks--; /* Compute # of blocks */ X for (x = blocks; x >= 0 ; x--) { X lseek(fi, (long) (x * 256), 0); X read(fi, buffer, 256); /* Read block */ X for (y = 255; y >= 0; y--) /* Encrypt/decrypt block */ X bufout[y] = enchar(buffer[y]); X lseek(fo, (long) (x * 256), 0); X if (jcount != 0) { /* Write/count block */ X if (jcount > 256) X jcount -= 256; X else { X write(fo, bufout, (256 - jcount)); X jcount = 0; X } X } else X write(fo, bufout, 256); X } X if (encrypt != 0) { X buffer[0] = MARKER; X buffer[1] = MARKER; /* Make 3 255's */ X buffer[2] = MARKER; X lseek(fo, 0L, 2); /* Seek EOF */ X write(fo, buffer, 3); X } X close(fo); X close(fi); X puts("Complete."); X exit(0); X} SHAR_EOF sed 's/^X//' << 'SHAR_EOF' > text.uue Xbegin 660 text.enc XM9SX53U,O-UU43DL!05Q=%$`<5T)C8C8\2408%59N*C8`%T<>#155'@XR1QH! XM"@%23S]/!@<,%#1%4P0)&D!*"P$!``%810@.$E@#&F=;'`M84A8-%40&!@@1 XM,%(2#@U424)/$$@03PI(#1YK1!L<,EP<#0Q4&P`(#E,',A%C4Q<)!1E8#0H, XM11I)#`$(%1!$$!IG3AH7%T\63A)/4T0+&"<`!AE(`TE8!T14'08*#$P.%%1X XM'"]`%@I86!P;1TD34`!4)TX<#4D&3@T7"E14!AE7"$T!`1`&)T`6"EE!!D\' XM``!*!1DN``$,'Q522$%N*C8`%U0-#Q4`/RT4`U0G%D)=3@584D,(%2I-4@<; XM55="!P9$5!X03@,)040''2]`$D\;50$&"$4`5$0=+54!&D@55`Q'5Q%'1W(4 XM6UM-&4I?=P-4`0H!!0<&`1=+!!TO`1,<25T/`T`,21H>30$("!)70TXM3P<# XM4`Y23R]&4UX+`&)!`0Q(`$A)3R)H)CTL*QT($E(<`&=8'`%84Q8>"%,&544` XM+`$'&TD"25@'1$$;3QQ8#0\5`!$`-E=5`!\`!@</4U-!#1DG`%H-``%)2!Q% XM408'%E4("4!.`60B0A4'%$073A-.4E,,$&)!$08>$0!-"P!2$!P*"4P5#E52 XM&"]"&4\+11$*#E<7!D%&=@]"6&-_:$,-"500"QP!!`-`51L'-`\2!Q1$4@X4 XM15-4"Q@G```<!1E!7A9$4P$.#4D?&`A#`4\G31L;#`$<&Q4!`5\6`"9,4@<? XM$%,-&@U$?@(94AE-!D4%3R)/#!Q901P+1D%32P$!-D4!21\<24\'1%<4'%A, XM#`0,1!=.,T!4'1=,%DX.3P9#%Q$P51<,205$7@$7`!1/#D4)!VM/`$\U054- XM&$,95$94&T(7$&)!`0U)'$]9"PM%$0I83@,!&0$'`6=-`0<415,:#T1250T/ XM)P`<#T@`2$E/`DD9"EE4`TP`*@`*)UT:`1A"'@I&31=0`!AM`5(_!@!-20!" XM5509&4\9311.4P,F1!%.$512&PE/4T(%!CL`'`=(#4]93PM5`4\-2`D>!`QX XM&"A:&`I85A91;2M?"TA9;@$P#0X<3PT(#$T8"PH!0$!-#%YD3!9!6TP04A\' XM1Q8'!Q0A2!9)`!U47V5$`%5>2AA?7$%0$@DB#Q</&TD63@I(`54`!TD!4DA) XM50$-7D51!@$;4DT>%T`#'B)+5`<6*U-.1P%2!D5$8U$`!PH&`5X9!%$$"QP! XM`A@4*U)/9A]&5TH04@D/3!94'08V11Y)&!5'24\6110+"BI,34`!4TYV'%0= XM#T`#3@93%T=%!")&%T@;$$!)'6\!5$Y8`4Q?4@`!&"=>50X+11-/%D$40D0" XM,$D'#!M^``Q/1`!"74$!'0P'1`!.-4H7`AE('@L#`114"AEC1P`-#%5-1!T1 XM*U5/2!547E$14A\G21`<65,:#A1%%P<``"<`!P9(%T]<%TA.&D,/4P09!0$5 XM`35$?DY8`5-.1P%+!A45)$0!2!H=0%X*```1&AP`&`-!0Q,,+DM5!Q!4`65& XM`%,14T)U$%,:`!532`I%414)'5)-#@]1&@LC)51<0!-&7%804E8$$R8!%`D= XM&%1?940`1UA,%EY504,"&F9-&@$-10H;1E,$3A`6*T0!8DE$&1U:5QA,3E!/ XM`@-`0A\!)$1=3AQ$!0<$1%)."@$G4@$<&`!3)D]$$T=62Q!:3!52$Q\U)$1> XM215`6E$34E4<!S=$'T@*%$U!'6\K5$Y8`3@D)`%33A=F,$Y8<2,F(@!39$15 XM8@`@/2$Y90P[,'E53RUI(2E!8STB"V\[*W,`4TX,0`!*145W&$5>2405%%U7 XM`51>6!-?5U,825Y_#U1>2@%27UP10`=+`#%27`L!&@]:!D0/!1T/#P<-$TQ= XM02=<`08;319.;0$!7Q85)TQ22%],$A].10%46DH!351`$$M4=!I.7$H`4E]7 XM`%,>7D1Z```0&P!!6$]N`%5/$D$>`$$11E9U'%5/6`%'74<!0@971GD22E)< XM10$-7E<!5%Y"$5Q-34H`!F<E5$Y82A(="@!"$E=`<@!"6E]`$0Q;70!'74,5 XM7E93$E)/=AU53TD:0EQ&5QL&$ATM3Q<:27\!#4X.0`8"6!!865,04U]R'$%> XM6!=*3E0221)13W,34TE81P`,7UX014\)4TQ!`$043TPD-`$=`!-/%$$<0@H9 XM8W47$!U52$,-"50'!Q=/3&=J:1I.,T<1'!P!>&4G3!\'"Q-B61P<2!Q!6@I$ XM4A`>#$4?&`1$4@8I21L<%4`'!PA/4DD+5"Q4`$@(`%5"#`14$$Y?1@D8%5E5 XM3S1+!0,80Q<"`TX'+104(4L2#@U:``PG`5(020L!&04%`1`;-5T:"PP!&@`! XM3EX&!`=C5A=(`1172$X,5$]E<T],)14`$QHA0Q`!#5-2(@]#`4@4&C!45!I( XM$T19&AP-5`\614T)#T0`3BE``$X=3P<'%40>7T4&)E`?"`L1`$4;2@!5-A95 XM9DQ!50$*9EH=!@H`'0$#`!Q)1`TL5`!(#1Q`00<+`1@'%D0>0VI.4SHO2E08 XM'5,`!PE.4V4-&2X`)0@"'P!9'`%350<84TP.!$4<3S5;!0H*4A8*`D5>!A(= XM-TE2`00%4T(8`$5^3EA"#!T!0QH"+UH<"@H.4D]L3U-N$%4K4U,('A5)0`X& XM3!!/&%1,`P\!$`8F71,+6$``3@8!`E0`&2I4'T@>'%5%3A%)$4\)51X/"4$! XM"F9!$T\-2!=E1@`52`@9+5<:!P]44%\!`507&@L;9V0/`3(E!'Q41A=4`4X% XM0P$&%ALE504)&Q$/808'4AH?%E(80$$3$%YI'1==6092/"5O4W\!&RM84CY` XM?RA"3C%@,$Y0<@X"0'D6`"Y75#A83AT"'@AX+PI4`D\+2"0<0E\!%4X&&EAT XM(R0X`1P<9WPW(5AY%@`/6%-!$1DN```0&P!%04]N*55/44D)5D%D%QEF?0P< XM60M2/1-.!TX)$&)/`4D+&TU<`P%4$$9S*C\)$4$`#C-*&!=4`0<&`@$1218` XM8T@!2$U`$0->50]43C%530X/3!8<9ED<&Q$`$T\52!Q5$%4F10`*&AU06`8+ XM3E4`'P!G`A!$`0\S1AL`6$`="D=('%41!C9"!@$&&U(-1A=%%`,564!,"%12 XM!BA=`0X53`%/#TY3%408*TX&'0T'`1=#3"M^)QX!&@A`0A(`9TT13A='4P8" XM30(&`1LM!@9(`1!311L%5!!/#4],!`Y,'@HT#W]E4P!:/"5O4W\!&RM84@$: XM54`-&A=`$`L50!\&0$X53C-'$4XK0!T:!@$Q5!$/8F\##!H55$4`"@!33S1) XM#QX.4QT),@=_3UD(/P8%4AQ7"P<V`"8G(2P`11Q$054;"T$("0Q!``1F01-/ XM.'14.T9B%DL(52Y!$1I(4@!A!@=2&A\64AA`06D<#&@'?W3Q=*<;;L-O@M/6 XM@D)Y]MAKL)9@#"ZKWHU)GP@9:..`>E@`M`DHQIM:8\&I_$Z3J))2@F4(Q!-* XM:5')CI_?NL4:TE$*PRAE'HC?/'^*,?1*1F1VD+6S@$J++V0,H%7'[!9BMM"@ XM_\N1)Y'6"0S%+<D.?4(Y+T^?QAZ\!^7.`-';"+!)XV1)QC#.>,/SDX##<4FX XMHV#E27L!`+(L\'B9._T#^6^>#H[75IY31Y=(F3>L[IOF;J.\<4AO0,-_C:NQ XMR\-I>[HGW-F4\L3#)KH??C86C>ZF?#<!&H!?(X,K1?X,*WT*/K/.&H3WC/T: XMY,Z7=`+Q-%T:P(*0-,*2V68IJFVL<O(10&N;BQ9(^NZMI[O9Q-6O](<E?"$( XMN5LI9`1QH&MG-O2JV'Q6::OW7DJ*TK,Q8FV,S,AHWR]`M;E>J&*6H1&=N,BY XM=Y*D8,:WE:*M=I<D@#"&JY=ME,R$='U^WJGED<:<5WGE+7OL6VA7>."]<H?E XMJLCHY)#>U447!A^8C]I-Y;&A@05K6*8.BO";MJ_-9/]$M]2)D:8?L\$9(![& XM@K/!7\>&XG#Y[(1*WG"+E]2C6,:KZ^F?!>X4,T4F%3V'1+CAF^ORVL[R/>"= XMAI4UUD3=`\\V\V_$U-IHDC-7N2,4.N=ZNS?JVQ7ZL[^$2J-H277K(+?RW-&$ XMPZ23YD5IV5?#^&7_O@[+4MN=N^$:%F^;%YK@,W:&!Y/\YR(L_A@GR3TXO]/C XMZ081L&(`L&;8]!\$&3(C)%W2,!"S'$E_H37^AL+\MMZ7"R+XQ@V5#QUAMN8# XMY"\\Q4CX$5,8PC-PC[WNM&G,+:H7C[PPBS_$K0SAB,47C_74>33#A]0JOW21 XMU/H]HV=9<0T/$)5A-@HE?#(G6FO@R:^HR!3>^,I7<RP*JVI&BV6N\DF_FOXJ XMI1%3>_I[2C3U'DQ$XN!)7,82N<:LB/E9UCLHZQ5\?)NK7WRFOTLQ]7G;\D"` XH`_1.*Q')UTO%)&T-B+</<;823G?'*]]1`T?CFM*UI&DM-*TNL/___WG; X` Xend SHAR_EOF exit