scramble.c revision 17721
117721Speter/*
217721Speter * Trivially encode strings to protect them from innocent eyes (i.e.,
317721Speter * inadvertent password compromises, like a network administrator
417721Speter * who's watching packets for legitimate reasons and accidentally sees
517721Speter * the password protocol go by).
617721Speter *
717721Speter * This is NOT secure encryption.
817721Speter *
917721Speter * It would be tempting to encode the password according to username
1017721Speter * and repository, so that the same password would encode to a
1117721Speter * different string when used with different usernames and/or
1217721Speter * repositories.  However, then users would not be able to cut and
1317721Speter * paste passwords around.  They're not supposed to anyway, but we all
1417721Speter * know they will, and there's no reason to make it harder for them if
1517721Speter * we're not trying to provide real security anyway.
1617721Speter */
1717721Speter
1817721Speter/* Set this to test as a standalone program. */
1917721Speter/* #define DIAGNOSTIC */
2017721Speter
2117721Speter#ifndef DIAGNOSTIC
2217721Speter#include "cvs.h"
2317721Speter#else /* ! DIAGNOSTIC */
2417721Speter/* cvs.h won't define this for us */
2517721Speter#define AUTH_CLIENT_SUPPORT
2617721Speter#define xmalloc malloc
2717721Speter/* Use "gcc -fwritable-strings". */
2817721Speter#include <stdio.h>
2917721Speter#include <stdio.h>
3017721Speter#include <string.h>
3117721Speter#endif /* ! DIAGNOSTIC */
3217721Speter
3317721Speter#if defined(AUTH_CLIENT_SUPPORT) || defined(AUTH_SERVER_SUPPORT)
3417721Speter
3517721Speter/* Map characters to each other randomly and symmetrically, A <--> B.
3617721Speter *
3717721Speter * We divide the ASCII character set into 3 domains: control chars (0
3817721Speter * thru 31), printing chars (32 through 126), and "meta"-chars (127
3917721Speter * through 255).  The control chars map _to_ themselves, the printing
4017721Speter * chars map _among_ themselves, and the meta chars map _among_
4117721Speter * themselves.  Why is this thus?
4217721Speter *
4317721Speter * No character in any of these domains maps to a character in another
4417721Speter * domain, because I'm not sure what characters are legal in
4517721Speter * passwords, or what tools people are likely to use to cut and paste
4617721Speter * them.  It seems prudent not to introduce control or meta chars,
4717721Speter * unless the user introduced them first.  And having the control
4817721Speter * chars all map to themselves insures that newline and
4917721Speter * carriage-return are safely handled.
5017721Speter */
5117721Speter
5217721Speterstatic unsigned char
5317721Spetershifts[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
5417721Speter17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 114, 120,
5517721Speter53, 79, 96, 109, 72, 108, 70, 64, 76, 67, 116, 74, 68, 87, 111, 52,
5617721Speter75, 119, 49, 34, 82, 81, 95, 65, 112, 86, 118, 110, 122, 105, 41, 57,
5717721Speter83, 43, 46, 102, 40, 89, 38, 103, 45, 50, 42, 123, 91, 35, 125, 55,
5817721Speter54, 66, 124, 126, 59, 47, 92, 71, 115, 78, 88, 107, 106, 56, 36, 121,
5917721Speter117, 104, 101, 100, 69, 73, 99, 63, 94, 93, 39, 37, 61, 48, 58, 113,
6017721Speter32, 90, 44, 98, 60, 51, 33, 97, 62, 77, 84, 80, 85, 223, 225, 216,
6117721Speter187, 166, 229, 189, 222, 188, 141, 249, 148, 200, 184, 136, 248, 190,
6217721Speter199, 170, 181, 204, 138, 232, 218, 183, 255, 234, 220, 247, 213, 203,
6317721Speter226, 193, 174, 172, 228, 252, 217, 201, 131, 230, 197, 211, 145, 238,
6417721Speter161, 179, 160, 212, 207, 221, 254, 173, 202, 146, 224, 151, 140, 196,
6517721Speter205, 130, 135, 133, 143, 246, 192, 159, 244, 239, 185, 168, 215, 144,
6617721Speter139, 165, 180, 157, 147, 186, 214, 176, 227, 231, 219, 169, 175, 156,
6717721Speter206, 198, 129, 164, 150, 210, 154, 177, 134, 127, 182, 128, 158, 208,
6817721Speter162, 132, 167, 209, 149, 241, 153, 251, 237, 236, 171, 195, 243, 233,
6917721Speter253, 240, 194, 250, 191, 155, 142, 137, 245, 235, 163, 242, 178, 152 };
7017721Speter
7117721Speter
7217721Speter/* SCRAMBLE and DESCRAMBLE work like this:
7317721Speter *
7417721Speter * scramble(STR) returns SCRM, a scrambled copy of STR.  SCRM[0] is a
7517721Speter * single letter indicating the scrambling method.  As of this
7617721Speter * writing, the only legal method is 'A', but check the code for more
7717721Speter * up-to-date information.  The copy will have been allocated with
7817721Speter * malloc().
7917721Speter *
8017721Speter * descramble(SCRM) returns STR, again in its own malloc'd space.
8117721Speter * descramble() uses SCRM[0] to determine which method of unscrambling
8217721Speter * to use.  If it does not recognize the method, it dies with error.
8317721Speter */
8417721Speter
8517721Speter/* Return a malloc'd, scrambled version of STR. */
8617721Speterchar *
8717721Speterscramble (str)
8817721Speter     char *str;
8917721Speter{
9017721Speter  int i;
9117721Speter  char *s;
9217721Speter
9317721Speter  /* +2 to hold the 'A' prefix that indicates which version of
9417721Speter   * scrambling this is (the first, obviously, since we only do one
9517721Speter   * kind of scrambling so far), and then the '\0' of course.
9617721Speter   */
9717721Speter  s = (char *) xmalloc (strlen (str) + 2);
9817721Speter
9917721Speter  s[0] = 'A';   /* Scramble (TM) version prefix. */
10017721Speter  strcpy (s + 1, str);
10117721Speter
10217721Speter  for (i = 1; s[i]; i++)
10317721Speter    s[i] = shifts[(unsigned char)(s[i])];
10417721Speter
10517721Speter  return s;
10617721Speter}
10717721Speter
10817721Speter/* Decode the string in place. */
10917721Speterchar *
11017721Speterdescramble (str)
11117721Speter     char *str;
11217721Speter{
11317721Speter  char *s;
11417721Speter  int i;
11517721Speter
11617721Speter  /* For now we can only handle one kind of scrambling.  In the future
11717721Speter   * there may be other kinds, and this `if' will become a `switch'.
11817721Speter   */
11917721Speter  if (str[0] != 'A')
12017721Speter#ifndef DIAGNOSTIC
12117721Speter    error (1, 0, "descramble: unknown scrambling method");
12217721Speter#else  /* DIAGNOSTIC */
12317721Speter  {
12417721Speter    fprintf (stderr, "descramble: unknown scrambling method\n", str);
12517721Speter    fflush (stderr);
12617721Speter    exit (EXIT_FAILURE);
12717721Speter  }
12817721Speter#endif  /* DIAGNOSTIC */
12917721Speter
13017721Speter  /* Method `A' is symmetrical, so scramble again to decrypt. */
13117721Speter  s = scramble (str + 1);
13217721Speter
13317721Speter  /* Shift the whole string one char to the left, pushing the unwanted
13417721Speter     'A' off the left end.  Safe, because s is null-terminated. */
13517721Speter  for (i = 0; s[i]; i++)
13617721Speter      s[i] = s[i + 1];
13717721Speter
13817721Speter  return s;
13917721Speter}
14017721Speter
14117721Speter#endif /* (AUTH_CLIENT_SUPPORT || AUTH_SERVER_SUPPORT) from top of file */
14217721Speter
14317721Speter#ifdef DIAGNOSTIC
14417721Speterint
14517721Spetermain ()
14617721Speter{
14717721Speter  int i;
14817721Speter  char *e, *m, biggie[256];
14917721Speter
15017721Speter  char *cleartexts[5];
15117721Speter  cleartexts[0] = "first";
15217721Speter  cleartexts[1] = "the second";
15317721Speter  cleartexts[2] = "this is the third";
15417721Speter  cleartexts[3] = "$#% !!\\3";
15517721Speter  cleartexts[4] = biggie;
15617721Speter
15717721Speter  /* Set up the most important test string: */
15817721Speter  /* Can't have a real ASCII zero in the string, because we want to
15917721Speter     use printf, so we substitute the character zero. */
16017721Speter  biggie[0] = '0';
16117721Speter  /* The rest of the string gets straight ascending ASCII. */
16217721Speter  for (i = 1; i < 256; i++)
16317721Speter    biggie[i] = i;
16417721Speter
16517721Speter  /* Test all the strings. */
16617721Speter  for (i = 0; i < 5; i++)
16717721Speter    {
16817721Speter      printf ("clear%d: %s\n", i, cleartexts[i]);
16917721Speter      e = scramble (cleartexts[i]);
17017721Speter      printf ("scram%d: %s\n", i, e);
17117721Speter      m = descramble (e);
17217721Speter      free (e);
17317721Speter      printf ("clear%d: %s\n\n", i, m);
17417721Speter      free (m);
17517721Speter    }
17617721Speter
17717721Speter  fflush (stdout);
17817721Speter  return 0;
17917721Speter}
18017721Speter#endif /* DIAGNOSTIC */
18117721Speter
18217721Speter/*
18317721Speter * ;;; The Emacs Lisp that did the dirty work ;;;
18417721Speter * (progn
18517721Speter *
18617721Speter *   ;; Helper func.
18717721Speter *   (defun random-elt (lst)
18817721Speter *     (let* ((len (length lst))
18917721Speter *            (rnd (random len)))
19017721Speter *       (nth rnd lst)))
19117721Speter *
19217721Speter *   ;; A list of all characters under 127, each appearing once.
19317721Speter *   (setq non-meta-chars
19417721Speter *         (let ((i 0)
19517721Speter *               (l nil))
19617721Speter *           (while (< i 127)
19717721Speter *             (setq l (cons i l)
19817721Speter *                   i (1+ i)))
19917721Speter *           l))
20017721Speter *
20117721Speter *   ;; A list of all characters 127 and above, each appearing once.
20217721Speter *   (setq meta-chars
20317721Speter *         (let ((i 127)
20417721Speter *               (l nil))
20517721Speter *           (while (< i 256)
20617721Speter *             (setq l (cons i l)
20717721Speter *                   i (1+ i)))
20817721Speter *           l))
20917721Speter *
21017721Speter *   ;; A vector that will hold the chars in a random order.
21117721Speter *   (setq scrambled-chars (make-vector 256 0))
21217721Speter *
21317721Speter *   ;; These characters should map to themselves.
21417721Speter *   (let ((i 0))
21517721Speter *     (while (< i 32)
21617721Speter *       (aset scrambled-chars i i)
21717721Speter *       (setq non-meta-chars (delete i non-meta-chars)
21817721Speter *             i (1+ i))))
21917721Speter *
22017721Speter *   ;; Assign random (but unique) values, within the non-meta chars.
22117721Speter *   (let ((i 32))
22217721Speter *     (while (< i 127)
22317721Speter *       (let ((ch (random-elt non-meta-chars)))
22417721Speter *         (if (= 0 (aref scrambled-chars i))
22517721Speter *             (progn
22617721Speter *               (aset scrambled-chars i ch)
22717721Speter *               (aset scrambled-chars ch i)
22817721Speter *               (setq non-meta-chars (delete ch non-meta-chars)
22917721Speter *                     non-meta-chars (delete i non-meta-chars))))
23017721Speter *         (setq i (1+ i)))))
23117721Speter *
23217721Speter *   ;; Assign random (but unique) values, within the non-meta chars.
23317721Speter *   (let ((i 127))
23417721Speter *     (while (< i 256)
23517721Speter *       (let ((ch (random-elt meta-chars)))
23617721Speter *         (if (= 0 (aref scrambled-chars i))
23717721Speter *             (progn
23817721Speter *               (aset scrambled-chars i ch)
23917721Speter *               (aset scrambled-chars ch i)
24017721Speter *               (setq meta-chars (delete ch meta-chars)
24117721Speter *                     meta-chars (delete i meta-chars))))
24217721Speter *         (setq i (1+ i)))))
24317721Speter *
24417721Speter *   ;; Now use the `scrambled-chars' vector to get your C array.
24517721Speter *   )
24617721Speter */
247