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
5332785Spetershifts[] = {
5432785Speter    0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15,
5532785Speter   16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
5632785Speter  114,120, 53, 79, 96,109, 72,108, 70, 64, 76, 67,116, 74, 68, 87,
5732785Speter  111, 52, 75,119, 49, 34, 82, 81, 95, 65,112, 86,118,110,122,105,
5832785Speter   41, 57, 83, 43, 46,102, 40, 89, 38,103, 45, 50, 42,123, 91, 35,
5932785Speter  125, 55, 54, 66,124,126, 59, 47, 92, 71,115, 78, 88,107,106, 56,
6032785Speter   36,121,117,104,101,100, 69, 73, 99, 63, 94, 93, 39, 37, 61, 48,
6132785Speter   58,113, 32, 90, 44, 98, 60, 51, 33, 97, 62, 77, 84, 80, 85,223,
6232785Speter  225,216,187,166,229,189,222,188,141,249,148,200,184,136,248,190,
6332785Speter  199,170,181,204,138,232,218,183,255,234,220,247,213,203,226,193,
6432785Speter  174,172,228,252,217,201,131,230,197,211,145,238,161,179,160,212,
6532785Speter  207,221,254,173,202,146,224,151,140,196,205,130,135,133,143,246,
6632785Speter  192,159,244,239,185,168,215,144,139,165,180,157,147,186,214,176,
6732785Speter  227,231,219,169,175,156,206,198,129,164,150,210,154,177,134,127,
6832785Speter  182,128,158,208,162,132,167,209,149,241,153,251,237,236,171,195,
6932785Speter  243,233,253,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
78109655Speter * xmalloc().
7917721Speter *
80109655Speter * descramble(SCRM) returns STR, again in its own xmalloc'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
85109655Speter/* Return a xmalloc'd, scrambled version of STR. */
8617721Speterchar *
8717721Speterscramble (str)
8832785Speter    char *str;
8917721Speter{
9032785Speter    int i;
9132785Speter    char *s;
9217721Speter
9332785Speter    /* +2 to hold the 'A' prefix that indicates which version of
9432785Speter       scrambling this is (the first, obviously, since we only do one
9532785Speter       kind of scrambling so far), and then the '\0' of course.  */
9632785Speter    s = (char *) xmalloc (strlen (str) + 2);
9717721Speter
9832785Speter    /* Scramble (TM) version prefix. */
9932785Speter    s[0] = 'A';
10032785Speter    strcpy (s + 1, str);
10117721Speter
10232785Speter    for (i = 1; s[i]; i++)
10332785Speter	s[i] = shifts[(unsigned char)(s[i])];
10417721Speter
10532785Speter    return s;
10617721Speter}
10717721Speter
10817721Speter/* Decode the string in place. */
10917721Speterchar *
11017721Speterdescramble (str)
11132785Speter    char *str;
11217721Speter{
11332785Speter    char *s;
11432785Speter    int i;
11517721Speter
11632785Speter    /* For now we can only handle one kind of scrambling.  In the future
11732785Speter       there may be other kinds, and this `if' will become a `switch'.  */
11832785Speter    if (str[0] != 'A')
11917721Speter#ifndef DIAGNOSTIC
12032785Speter	error (1, 0, "descramble: unknown scrambling method");
12117721Speter#else  /* DIAGNOSTIC */
12232785Speter    {
12332785Speter	fprintf (stderr, "descramble: unknown scrambling method\n", str);
12432785Speter	fflush (stderr);
12532785Speter	exit (EXIT_FAILURE);
12632785Speter    }
12717721Speter#endif  /* DIAGNOSTIC */
12817721Speter
12932785Speter    /* Method `A' is symmetrical, so scramble again to decrypt. */
13032785Speter    s = scramble (str + 1);
13117721Speter
13232785Speter    /* Shift the whole string one char to the left, pushing the unwanted
13332785Speter       'A' off the left end.  Safe, because s is null-terminated. */
13432785Speter    for (i = 0; s[i]; i++)
13532785Speter	s[i] = s[i + 1];
13617721Speter
13732785Speter    return s;
13817721Speter}
13917721Speter
14017721Speter#endif /* (AUTH_CLIENT_SUPPORT || AUTH_SERVER_SUPPORT) from top of file */
14117721Speter
14217721Speter#ifdef DIAGNOSTIC
14317721Speterint
14417721Spetermain ()
14517721Speter{
14632785Speter    int i;
14732785Speter    char *e, *m, biggie[256];
14817721Speter
14932785Speter    char *cleartexts[5];
15032785Speter    cleartexts[0] = "first";
15132785Speter    cleartexts[1] = "the second";
15232785Speter    cleartexts[2] = "this is the third";
15332785Speter    cleartexts[3] = "$#% !!\\3";
15432785Speter    cleartexts[4] = biggie;
15517721Speter
15632785Speter    /* Set up the most important test string: */
15732785Speter    /* Can't have a real ASCII zero in the string, because we want to
15832785Speter       use printf, so we substitute the character zero. */
15932785Speter    biggie[0] = '0';
16032785Speter    /* The rest of the string gets straight ascending ASCII. */
16132785Speter    for (i = 1; i < 256; i++)
16232785Speter	biggie[i] = i;
16332785Speter
16432785Speter    /* Test all the strings. */
16532785Speter    for (i = 0; i < 5; i++)
16617721Speter    {
16732785Speter	printf ("clear%d: %s\n", i, cleartexts[i]);
16832785Speter	e = scramble (cleartexts[i]);
16932785Speter	printf ("scram%d: %s\n", i, e);
17032785Speter	m = descramble (e);
17132785Speter	free (e);
17232785Speter	printf ("clear%d: %s\n\n", i, m);
17332785Speter	free (m);
17417721Speter    }
17517721Speter
17632785Speter    fflush (stdout);
17732785Speter    return 0;
17817721Speter}
17917721Speter#endif /* DIAGNOSTIC */
18017721Speter
18117721Speter/*
18217721Speter * ;;; The Emacs Lisp that did the dirty work ;;;
18317721Speter * (progn
18417721Speter *
18517721Speter *   ;; Helper func.
18617721Speter *   (defun random-elt (lst)
18717721Speter *     (let* ((len (length lst))
18817721Speter *            (rnd (random len)))
18917721Speter *       (nth rnd lst)))
19017721Speter *
19117721Speter *   ;; A list of all characters under 127, each appearing once.
19217721Speter *   (setq non-meta-chars
19317721Speter *         (let ((i 0)
19417721Speter *               (l nil))
19517721Speter *           (while (< i 127)
19617721Speter *             (setq l (cons i l)
19717721Speter *                   i (1+ i)))
19817721Speter *           l))
19917721Speter *
20017721Speter *   ;; A list of all characters 127 and above, each appearing once.
20117721Speter *   (setq meta-chars
20217721Speter *         (let ((i 127)
20317721Speter *               (l nil))
20417721Speter *           (while (< i 256)
20517721Speter *             (setq l (cons i l)
20617721Speter *                   i (1+ i)))
20717721Speter *           l))
20817721Speter *
20917721Speter *   ;; A vector that will hold the chars in a random order.
21017721Speter *   (setq scrambled-chars (make-vector 256 0))
21117721Speter *
21217721Speter *   ;; These characters should map to themselves.
21317721Speter *   (let ((i 0))
21417721Speter *     (while (< i 32)
21517721Speter *       (aset scrambled-chars i i)
21617721Speter *       (setq non-meta-chars (delete i non-meta-chars)
21717721Speter *             i (1+ i))))
21817721Speter *
21917721Speter *   ;; Assign random (but unique) values, within the non-meta chars.
22017721Speter *   (let ((i 32))
22117721Speter *     (while (< i 127)
22217721Speter *       (let ((ch (random-elt non-meta-chars)))
22317721Speter *         (if (= 0 (aref scrambled-chars i))
22417721Speter *             (progn
22517721Speter *               (aset scrambled-chars i ch)
22617721Speter *               (aset scrambled-chars ch i)
22717721Speter *               (setq non-meta-chars (delete ch non-meta-chars)
22817721Speter *                     non-meta-chars (delete i non-meta-chars))))
22917721Speter *         (setq i (1+ i)))))
23017721Speter *
23117721Speter *   ;; Assign random (but unique) values, within the non-meta chars.
23217721Speter *   (let ((i 127))
23317721Speter *     (while (< i 256)
23417721Speter *       (let ((ch (random-elt meta-chars)))
23517721Speter *         (if (= 0 (aref scrambled-chars i))
23617721Speter *             (progn
23717721Speter *               (aset scrambled-chars i ch)
23817721Speter *               (aset scrambled-chars ch i)
23917721Speter *               (setq meta-chars (delete ch meta-chars)
24017721Speter *                     meta-chars (delete i meta-chars))))
24117721Speter *         (setq i (1+ i)))))
24217721Speter *
24317721Speter *   ;; Now use the `scrambled-chars' vector to get your C array.
24417721Speter *   )
24517721Speter */
246