/* * Stealth V1.0 by Henry Hastur * * May or may not be covered by US ITAR encryption export regulations, if * in doubt, don't export it. It would be pretty stupid if it was, but * hey, governments have done some pretty stupid things before now.... * * This program is copyright Henry Hastur 1994, but may be freely distributed, * modified, incorporated into other programs and used, as long as the * copyright stays attached and you obey any relevant import or export * restrictions on the code. No warranty is offered, and no responsibility * is taken for any damage use of this program may cause. In other words, * do what you want with it, but don't expect me to pay up if anything * unexpected goes wrong - you're using it at your own risk... * */ #include #include #include #include #include #include #ifdef __SASC #undef getc #undef putchar #endif /* Few definitions from PGP for it's header/algorithm versions */ #define CURRENT_VERSION 0x02 #define RSA_VERSION 0x01 #define ID_SIZE 8 /* define TRUE and FALSE */ #define TRUE 1 #define FALSE 0 /* A byte */ #ifndef __SASC typedef unsigned char byte; #endif /* Few global variables */ static int verbose = FALSE; static int conventional = FALSE; static int adding = FALSE; static char file_name [1024]; static int file_open = FALSE; static FILE *afp; /* int_handler() - tidy up and exit */ static void int_handler(int unused) { long fpos; int c; /* If we've still got a file open */ if (file_open) { /* If we can still write to it, erase it */ if (afp) { fseek (afp, 0l, 2); fpos = ftell (afp); fseek (afp, 0l, 0); while (fpos --) putc (0, afp); } #ifndef UNIX /* Finally unlink it */ unlink (file_name); #endif } exit (1); } /* Read a PGP ctb-lengh */ static long read_length (int c, FILE *fp) { static int bytes [] = { 1, 2, 4, 0 }; long len = 0; int n; n = bytes [c & 0x03]; if (!n) return 0x7FFFFFFF; for (; n > 0 ; n--) { len *= 256; len += getc(fp); } return len; } /* Write a PGP ctb-length */ static void write_length (int ctb, unsigned long length, FILE *fp) { unsigned long mask; int bytes, shift,c; ctb &= 0xFC; if (length < 256) { mask = 0xFF; bytes = 1; shift = 0; } else if (length < 65536) { mask = 0xFF00; bytes = 2; shift = 8; ctb |= 1; } else { mask = 0xFF000000; shift = 24; bytes = 4; ctb |= 2; } putc (ctb, fp); while (bytes-- > 0) { c = ((length & mask) >> shift); mask >>= 8; shift -= 8; putc (c, fp); } } /* Hunt through pubring.pgp for the appropriate secret key */ #define ID_FROM_NAME 0 #define LENGTH_FROM_ID 1 static int find_key_id(byte *id, int *length, char *s, int type) { char *path; char pub_name [1024]; FILE *pub = NULL; int c1, c2, len, i, klen, c; char userid [256]; /* Find pubring.pgp */ if (path = getenv("PGPPATH")) { sprintf (pub_name, "%s/pubring.pgp", path); pub = fopen(pub_name, "rb"); } if (!pub) pub = fopen ("pubring.pgp", "rb"); if (!pub) { fprintf (stderr,"Can't find pubring.pgp in $PGPPATH or . : exiting !\n"); exit (1); } /* Read the contents till we find what we're looking for */ while ((c1 = getc(pub)) != EOF) { c2 = (c1 & 0xBC); switch (c2) { /* Secret key, probably revocation cert. */ case 0x94: len = read_length (c1, pub); for (; len > 0; len--) (void) getc (pub); break; /* Public key - grab id and length */ case 0x98: len = read_length (c1, pub); for (i = 0; i < 8; i++) (void) getc (pub); len -= 10; /* OK, here we are at the public modulus, get the size from the MPI header */ klen = getc(pub) * 256; klen += getc (pub); /* Return the length for the caller to use */ *length = klen; /* We only need the last 64 bits */ len -= ((klen + 7) / 8); i = (((klen + 7) / 8) - ID_SIZE); /* Skip unneccesary bytes */ for (; i > 0; i--) { (void) getc (pub); } if (type == ID_FROM_NAME) { for (i = 0; i < ID_SIZE; i++) { id [i] = getc(pub); } } else { /* Looking for length from ID */ int found_id = TRUE; for (i = 0; i < ID_SIZE; i++) { if (id[i] != getc(pub)) found_id = FALSE; } if (found_id) { fclose (pub); return TRUE; } } for (; len > 0 ; len--) { (void) getc (pub); } break; /* Keyring trust, comment */ case 0xB0: case 0xB8: len = getc (pub); for (; len > 0; len--) (void) getc (pub); break; /* USER ID ! */ case 0xB4: len = getc (pub); if (type == ID_FROM_NAME) { for (i = 0; i < len; i++) { c = getc (pub); userid [i] = tolower (c); } userid [i] = 0; if (strstr(userid, s)) { if (verbose) fprintf (stderr, "Found user: %s\n",userid); return TRUE; } } else for (i = 0; i < len; i++) (void) getc (pub); break; /* Anything we don't care about */ default: len = read_length (c1, pub); for (; len > 0; len--) (void) getc (pub); break; } } fclose (pub); /* Uh-oh, failed to find it ! */ return FALSE; } /* Strip_headers() : Should be obvious what this does, really ! */ static void strip_headers(FILE *fp) { int c1,c2; long len; int i; byte id [ID_SIZE]; int key_length; byte key_length_found = FALSE; byte rsa_written = FALSE; int mpi_length; /* Run through the whole message checking each packet */ while ((c1 = getc(fp)) != EOF) { c2 = (c1 & 0xBC); switch (c2) { /* Public key encoded packet */ case 0x84: /* Read length */ len = read_length(c1, fp); if (verbose) fprintf (stderr, "Found %d byte RSA packet.\n", len); /* We only support ONE RSA block ! This is because we have no idea of the file format when we start adding headers, so we have to assume that this is the case. Warn the user, then abort... */ if (rsa_written) { fprintf (stderr, "WARNING: More than one RSA block found... stripping extra block !\n"); /* Throw away the block */ ohno_abort_abort: while (len-- > 0) (void) getc (fp); break; } /* Check for conventional encryption specified */ if (conventional) { fprintf (stderr, "WARNING: You specified conventional encryption with an RSA-encrypted file !\nI hope you know what you're doing... stripping RSA header....\n"); goto ohno_abort_abort; } /* Check public key version byte */ c1 = getc (fp); if (c1 != CURRENT_VERSION) { fprintf(stderr, "Hmm, PK version %d not %d, may not decrypt at recipient\n", c1, CURRENT_VERSION); } /* Strip key ID */ for (i = 0; i < ID_SIZE; i++) id[i] = getc (fp); if (find_key_id (id,&key_length,NULL,LENGTH_FROM_ID)) { key_length_found = TRUE; } /* Check RSA version byte */ c1 = getc(fp); if (c1 != RSA_VERSION) { fprintf (stderr, "Hmm, RSA version %d not %d, may not decrypt at recipient\n", c1, RSA_VERSION); } /* Strip MPI prefix */ mpi_length = getc(fp); mpi_length = mpi_length * 256 + getc(fp); /* Now, we have a problem in that PGP may generate an RSA block shorter than your key, in which case decryption is likely to fail. Check for this and warn the user ! */ if (!key_length_found) { fprintf (stderr, "Hmm, couldn't get the length of this key, so can't verify that decryption\nwill be successful.\n"); } else { if (((mpi_length + 7) / 8) != ((key_length + 7) / 8)) { fprintf (stderr, "WARNING : Short RSA block output, decryption will probably fail if used !\n"); } } /* Copy remaining data from packet */ len -= 12; for (; len > 0; len--) putchar (getc(fp)); rsa_written = TRUE; break; /* IDEA packet */ case 0xA4: /* Read length */ len = read_length(c1, fp); if (verbose) fprintf (stderr, "Found %d byte IDEA packet.\n", len); /* Copy data from packet */ for (; len > 0; len--) putchar (getc (fp)); break; default: /* Oh no ! Don't know what this is - just skip it ! */ if (verbose) fprintf (stderr, "Oops ! Unexpected packet type, skipping !\n"); len = read_length (c1, fp); for (; len > 0; len --) (void) getc (fp); break; } } } /* Now we put the headers back in again */ static void add_headers(byte *id, int length) { unsigned long len, mask; int shift; int i, c; long fpos; long flen; int s; #ifdef USE_PGPPATH char *pgp_path; #endif /* Foo ! We have to use a temporary file, because we need to be able to output the length after reading it in ! */ #ifdef USE_TMP strcpy (file_name, "/tmp/stealth.t"); #else #ifdef USE_PGPPATH pgp_path = getenv ("PGPPATH"); if (!pgp_path) { fprintf (stderr, "PGPPATH not set !\n"); exit (1); } sprintf(file_name,"%s/stealth.t",pgp_path); #else strcpy (file_name, "stealth.t"); #endif #endif s = strlen (file_name); i = 0; while (!access (file_name, F_OK) && i < 100) { sprintf (file_name + s, "%d", i++); } afp = fopen (file_name,"w+b"); if (!afp) { fprintf (stderr, "Can't open '%s' !\n", file_name); exit (2); } /* On unix, unlink the file immediately, to improve security */ #ifdef UNIX unlink (file_name); #endif file_open = TRUE; if (!conventional) { /* First output the PK header */ len = 4 + ID_SIZE + (length + 7)/8; write_length (0x84, len, afp); putc (CURRENT_VERSION, afp); /* Store the key ID */ for (i = 0; i < 8; i++) { putc (id [i], afp); } /* RSA version */ putc (RSA_VERSION, afp); /* MPI header */ c = (length & 0xFF00) >> 8; putc (c, afp); putc (length & 0xFF, afp); /* Copy the MPI over */ i = (length + 7) / 8; while (i-- > 0) { c = getchar(); putc (c, afp); } } /* Now the IDEA bits */ len = 0xFFFFFFFF; fpos = ftell (afp) + 1; write_length (0xA4, len, afp); len = 0; while ((c = getchar ()) != EOF) { len ++; putc (c, afp); } fseek (afp, fpos, 0); /* Set up mask for length writing */ mask = 0xFF000000; shift = 24; /* Write the length back */ for (i = 0; i < 4; i++) { c = (len & mask) >> shift; shift -= 8; mask >>= 8; putc (c, afp); } /* OK, now let's output the data ! */ fseek (afp, 0l, 0); while ((c = getc (afp)) != EOF) { putchar (c); } /* Erase the file */ flen = ftell (afp); fseek (afp, 0l,0); while (flen --) putc (0, afp); fclose (afp); afp = NULL; #ifndef UNIX /* Finally, delete the temporary file */ unlink (file_name); #endif file_open = FALSE; } static char looking_for [256]; /* Do the stuff */ main(int argc, char *argv[]) { int length; byte id [ID_SIZE]; char *s, *d; int arg = 1,i; /* Set the umask for any files we may create */ #ifdef UNIX umask (077); #endif signal (SIGINT, int_handler); /* Check command line parameters */ while (arg != argc && argv [arg][0] == '-') { for (i = 1; argv[arg][i]; i++) { switch (argv[arg][i]) { case 'v': verbose = TRUE; break; case 'c': conventional = TRUE; break; case 'a': adding = TRUE; break; } } arg++; } if (!adding) strip_headers (stdin); else { if (!conventional) { if (arg == argc) { fprintf (stderr, "You specified -a, but gave no user id !\n"); exit (1); } s = argv[arg]; d = looking_for; while (*s) { *d++ = tolower (*s); s++; } *d = 0; } if (conventional || find_key_id (id,&length,looking_for,ID_FROM_NAME)) { add_headers (id, length); } else { fprintf (stderr, "Can't find key for user %s\n",argv[arg]); } } }