xref: /openbsd/gnu/usr.bin/cvs/src/scramble.c (revision 2286d8ed)
113571821Stholo /*
213571821Stholo  * Trivially encode strings to protect them from innocent eyes (i.e.,
313571821Stholo  * inadvertent password compromises, like a network administrator
413571821Stholo  * who's watching packets for legitimate reasons and accidentally sees
513571821Stholo  * the password protocol go by).
613571821Stholo  *
713571821Stholo  * This is NOT secure encryption.
813571821Stholo  *
913571821Stholo  * It would be tempting to encode the password according to username
1013571821Stholo  * and repository, so that the same password would encode to a
1113571821Stholo  * different string when used with different usernames and/or
1213571821Stholo  * repositories.  However, then users would not be able to cut and
1313571821Stholo  * paste passwords around.  They're not supposed to anyway, but we all
1413571821Stholo  * know they will, and there's no reason to make it harder for them if
1513571821Stholo  * we're not trying to provide real security anyway.
1613571821Stholo  */
1713571821Stholo 
1813571821Stholo /* Set this to test as a standalone program. */
1913571821Stholo /* #define DIAGNOSTIC */
2013571821Stholo 
2113571821Stholo #ifndef DIAGNOSTIC
2213571821Stholo #include "cvs.h"
2313571821Stholo #else /* ! DIAGNOSTIC */
2413571821Stholo /* cvs.h won't define this for us */
2513571821Stholo #define AUTH_CLIENT_SUPPORT
2613571821Stholo #define xmalloc malloc
2713571821Stholo /* Use "gcc -fwritable-strings". */
2813571821Stholo #include <stdio.h>
2913571821Stholo #include <stdio.h>
3013571821Stholo #include <string.h>
3113571821Stholo #endif /* ! DIAGNOSTIC */
3213571821Stholo 
3313571821Stholo #if defined(AUTH_CLIENT_SUPPORT) || defined(AUTH_SERVER_SUPPORT)
3413571821Stholo 
3513571821Stholo /* Map characters to each other randomly and symmetrically, A <--> B.
3613571821Stholo  *
3713571821Stholo  * We divide the ASCII character set into 3 domains: control chars (0
3813571821Stholo  * thru 31), printing chars (32 through 126), and "meta"-chars (127
3913571821Stholo  * through 255).  The control chars map _to_ themselves, the printing
4013571821Stholo  * chars map _among_ themselves, and the meta chars map _among_
4113571821Stholo  * themselves.  Why is this thus?
4213571821Stholo  *
4313571821Stholo  * No character in any of these domains maps to a character in another
4413571821Stholo  * domain, because I'm not sure what characters are legal in
4513571821Stholo  * passwords, or what tools people are likely to use to cut and paste
4613571821Stholo  * them.  It seems prudent not to introduce control or meta chars,
4713571821Stholo  * unless the user introduced them first.  And having the control
4813571821Stholo  * chars all map to themselves insures that newline and
4913571821Stholo  * carriage-return are safely handled.
5013571821Stholo  */
5113571821Stholo 
52c26070a5Stholo static unsigned char
53*2286d8edStholo shifts[] = {
54*2286d8edStholo     0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15,
55*2286d8edStholo    16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
56*2286d8edStholo   114,120, 53, 79, 96,109, 72,108, 70, 64, 76, 67,116, 74, 68, 87,
57*2286d8edStholo   111, 52, 75,119, 49, 34, 82, 81, 95, 65,112, 86,118,110,122,105,
58*2286d8edStholo    41, 57, 83, 43, 46,102, 40, 89, 38,103, 45, 50, 42,123, 91, 35,
59*2286d8edStholo   125, 55, 54, 66,124,126, 59, 47, 92, 71,115, 78, 88,107,106, 56,
60*2286d8edStholo    36,121,117,104,101,100, 69, 73, 99, 63, 94, 93, 39, 37, 61, 48,
61*2286d8edStholo    58,113, 32, 90, 44, 98, 60, 51, 33, 97, 62, 77, 84, 80, 85,223,
62*2286d8edStholo   225,216,187,166,229,189,222,188,141,249,148,200,184,136,248,190,
63*2286d8edStholo   199,170,181,204,138,232,218,183,255,234,220,247,213,203,226,193,
64*2286d8edStholo   174,172,228,252,217,201,131,230,197,211,145,238,161,179,160,212,
65*2286d8edStholo   207,221,254,173,202,146,224,151,140,196,205,130,135,133,143,246,
66*2286d8edStholo   192,159,244,239,185,168,215,144,139,165,180,157,147,186,214,176,
67*2286d8edStholo   227,231,219,169,175,156,206,198,129,164,150,210,154,177,134,127,
68*2286d8edStholo   182,128,158,208,162,132,167,209,149,241,153,251,237,236,171,195,
69*2286d8edStholo   243,233,253,240,194,250,191,155,142,137,245,235,163,242,178,152 };
7013571821Stholo 
7113571821Stholo 
7213571821Stholo /* SCRAMBLE and DESCRAMBLE work like this:
7313571821Stholo  *
7413571821Stholo  * scramble(STR) returns SCRM, a scrambled copy of STR.  SCRM[0] is a
7513571821Stholo  * single letter indicating the scrambling method.  As of this
7613571821Stholo  * writing, the only legal method is 'A', but check the code for more
7713571821Stholo  * up-to-date information.  The copy will have been allocated with
7813571821Stholo  * malloc().
7913571821Stholo  *
8013571821Stholo  * descramble(SCRM) returns STR, again in its own malloc'd space.
8113571821Stholo  * descramble() uses SCRM[0] to determine which method of unscrambling
8213571821Stholo  * to use.  If it does not recognize the method, it dies with error.
8313571821Stholo  */
8413571821Stholo 
8513571821Stholo /* Return a malloc'd, scrambled version of STR. */
8613571821Stholo char *
scramble(str)8713571821Stholo scramble (str)
8813571821Stholo     char *str;
8913571821Stholo {
9013571821Stholo     int i;
9113571821Stholo     char *s;
9213571821Stholo 
9313571821Stholo     /* +2 to hold the 'A' prefix that indicates which version of
94*2286d8edStholo        scrambling this is (the first, obviously, since we only do one
95*2286d8edStholo        kind of scrambling so far), and then the '\0' of course.  */
96c26070a5Stholo     s = (char *) xmalloc (strlen (str) + 2);
9713571821Stholo 
98*2286d8edStholo     /* Scramble (TM) version prefix. */
99*2286d8edStholo     s[0] = 'A';
10013571821Stholo     strcpy (s + 1, str);
10113571821Stholo 
10213571821Stholo     for (i = 1; s[i]; i++)
103c26070a5Stholo 	s[i] = shifts[(unsigned char)(s[i])];
10413571821Stholo 
10513571821Stholo     return s;
10613571821Stholo }
10713571821Stholo 
10813571821Stholo /* Decode the string in place. */
10913571821Stholo char *
descramble(str)11013571821Stholo descramble (str)
11113571821Stholo     char *str;
11213571821Stholo {
113c26070a5Stholo     char *s;
114c26070a5Stholo     int i;
11513571821Stholo 
11613571821Stholo     /* For now we can only handle one kind of scrambling.  In the future
117*2286d8edStholo        there may be other kinds, and this `if' will become a `switch'.  */
11813571821Stholo     if (str[0] != 'A')
11913571821Stholo #ifndef DIAGNOSTIC
12013571821Stholo 	error (1, 0, "descramble: unknown scrambling method");
12113571821Stholo #else  /* DIAGNOSTIC */
12213571821Stholo     {
12313571821Stholo 	fprintf (stderr, "descramble: unknown scrambling method\n", str);
12413571821Stholo 	fflush (stderr);
125c2c61682Stholo 	exit (EXIT_FAILURE);
12613571821Stholo     }
12713571821Stholo #endif  /* DIAGNOSTIC */
12813571821Stholo 
12913571821Stholo     /* Method `A' is symmetrical, so scramble again to decrypt. */
13013571821Stholo     s = scramble (str + 1);
13113571821Stholo 
132c26070a5Stholo     /* Shift the whole string one char to the left, pushing the unwanted
133c26070a5Stholo        'A' off the left end.  Safe, because s is null-terminated. */
134c26070a5Stholo     for (i = 0; s[i]; i++)
135c26070a5Stholo 	s[i] = s[i + 1];
13613571821Stholo 
137c26070a5Stholo     return s;
13813571821Stholo }
13913571821Stholo 
14013571821Stholo #endif /* (AUTH_CLIENT_SUPPORT || AUTH_SERVER_SUPPORT) from top of file */
14113571821Stholo 
14213571821Stholo #ifdef DIAGNOSTIC
14313571821Stholo int
main()14413571821Stholo main ()
14513571821Stholo {
14613571821Stholo     int i;
14713571821Stholo     char *e, *m, biggie[256];
14813571821Stholo 
14913571821Stholo     char *cleartexts[5];
15013571821Stholo     cleartexts[0] = "first";
15113571821Stholo     cleartexts[1] = "the second";
15213571821Stholo     cleartexts[2] = "this is the third";
15313571821Stholo     cleartexts[3] = "$#% !!\\3";
15413571821Stholo     cleartexts[4] = biggie;
15513571821Stholo 
15613571821Stholo     /* Set up the most important test string: */
15713571821Stholo     /* Can't have a real ASCII zero in the string, because we want to
15813571821Stholo        use printf, so we substitute the character zero. */
15913571821Stholo     biggie[0] = '0';
16013571821Stholo     /* The rest of the string gets straight ascending ASCII. */
16113571821Stholo     for (i = 1; i < 256; i++)
16213571821Stholo 	biggie[i] = i;
16313571821Stholo 
16413571821Stholo     /* Test all the strings. */
16513571821Stholo     for (i = 0; i < 5; i++)
16613571821Stholo     {
16713571821Stholo 	printf ("clear%d: %s\n", i, cleartexts[i]);
16813571821Stholo 	e = scramble (cleartexts[i]);
16913571821Stholo 	printf ("scram%d: %s\n", i, e);
17013571821Stholo 	m = descramble (e);
17113571821Stholo 	free (e);
17213571821Stholo 	printf ("clear%d: %s\n\n", i, m);
17313571821Stholo 	free (m);
17413571821Stholo     }
17513571821Stholo 
17613571821Stholo     fflush (stdout);
17713571821Stholo     return 0;
17813571821Stholo }
17913571821Stholo #endif /* DIAGNOSTIC */
18013571821Stholo 
18113571821Stholo /*
18213571821Stholo  * ;;; The Emacs Lisp that did the dirty work ;;;
18313571821Stholo  * (progn
18413571821Stholo  *
18513571821Stholo  *   ;; Helper func.
18613571821Stholo  *   (defun random-elt (lst)
18713571821Stholo  *     (let* ((len (length lst))
18813571821Stholo  *            (rnd (random len)))
18913571821Stholo  *       (nth rnd lst)))
19013571821Stholo  *
19113571821Stholo  *   ;; A list of all characters under 127, each appearing once.
19213571821Stholo  *   (setq non-meta-chars
19313571821Stholo  *         (let ((i 0)
19413571821Stholo  *               (l nil))
19513571821Stholo  *           (while (< i 127)
19613571821Stholo  *             (setq l (cons i l)
19713571821Stholo  *                   i (1+ i)))
19813571821Stholo  *           l))
19913571821Stholo  *
20013571821Stholo  *   ;; A list of all characters 127 and above, each appearing once.
20113571821Stholo  *   (setq meta-chars
20213571821Stholo  *         (let ((i 127)
20313571821Stholo  *               (l nil))
20413571821Stholo  *           (while (< i 256)
20513571821Stholo  *             (setq l (cons i l)
20613571821Stholo  *                   i (1+ i)))
20713571821Stholo  *           l))
20813571821Stholo  *
20913571821Stholo  *   ;; A vector that will hold the chars in a random order.
21013571821Stholo  *   (setq scrambled-chars (make-vector 256 0))
21113571821Stholo  *
21213571821Stholo  *   ;; These characters should map to themselves.
21313571821Stholo  *   (let ((i 0))
21413571821Stholo  *     (while (< i 32)
21513571821Stholo  *       (aset scrambled-chars i i)
21613571821Stholo  *       (setq non-meta-chars (delete i non-meta-chars)
21713571821Stholo  *             i (1+ i))))
21813571821Stholo  *
21913571821Stholo  *   ;; Assign random (but unique) values, within the non-meta chars.
22013571821Stholo  *   (let ((i 32))
22113571821Stholo  *     (while (< i 127)
22213571821Stholo  *       (let ((ch (random-elt non-meta-chars)))
22313571821Stholo  *         (if (= 0 (aref scrambled-chars i))
22413571821Stholo  *             (progn
22513571821Stholo  *               (aset scrambled-chars i ch)
22613571821Stholo  *               (aset scrambled-chars ch i)
22713571821Stholo  *               (setq non-meta-chars (delete ch non-meta-chars)
22813571821Stholo  *                     non-meta-chars (delete i non-meta-chars))))
22913571821Stholo  *         (setq i (1+ i)))))
23013571821Stholo  *
23113571821Stholo  *   ;; Assign random (but unique) values, within the non-meta chars.
23213571821Stholo  *   (let ((i 127))
23313571821Stholo  *     (while (< i 256)
23413571821Stholo  *       (let ((ch (random-elt meta-chars)))
23513571821Stholo  *         (if (= 0 (aref scrambled-chars i))
23613571821Stholo  *             (progn
23713571821Stholo  *               (aset scrambled-chars i ch)
23813571821Stholo  *               (aset scrambled-chars ch i)
23913571821Stholo  *               (setq meta-chars (delete ch meta-chars)
24013571821Stholo  *                     meta-chars (delete i meta-chars))))
24113571821Stholo  *         (setq i (1+ i)))))
24213571821Stholo  *
24313571821Stholo  *   ;; Now use the `scrambled-chars' vector to get your C array.
24413571821Stholo  *   )
24513571821Stholo  */
246