Merge from vendor branch LIBARCHIVE:
[dragonfly.git] / contrib / cvs-1.12 / src / scramble.c
1 /*
2  * Trivially encode strings to protect them from innocent eyes (i.e.,
3  * inadvertent password compromises, like a network administrator
4  * who's watching packets for legitimate reasons and accidentally sees
5  * the password protocol go by).
6  * 
7  * This is NOT secure encryption.
8  *
9  * It would be tempting to encode the password according to username
10  * and repository, so that the same password would encode to a
11  * different string when used with different usernames and/or
12  * repositories.  However, then users would not be able to cut and
13  * paste passwords around.  They're not supposed to anyway, but we all
14  * know they will, and there's no reason to make it harder for them if
15  * we're not trying to provide real security anyway.
16  */
17
18 /* Set this to test as a standalone program. */
19 /* #define DIAGNOSTIC */
20
21 #ifndef DIAGNOSTIC
22 #include "cvs.h"
23 #else /* ! DIAGNOSTIC */
24 /* cvs.h won't define this for us */
25 #define AUTH_CLIENT_SUPPORT
26 #define xmalloc malloc
27 /* Use "gcc -fwritable-strings". */
28 #include <stdio.h>
29 #include <stdio.h>
30 #include <string.h>
31 #endif /* ! DIAGNOSTIC */
32
33 #if defined(AUTH_CLIENT_SUPPORT) || defined(AUTH_SERVER_SUPPORT)
34
35 /* Map characters to each other randomly and symmetrically, A <--> B.
36  *
37  * We divide the ASCII character set into 3 domains: control chars (0
38  * thru 31), printing chars (32 through 126), and "meta"-chars (127
39  * through 255).  The control chars map _to_ themselves, the printing
40  * chars map _among_ themselves, and the meta chars map _among_
41  * themselves.  Why is this thus?
42  *
43  * No character in any of these domains maps to a character in another
44  * domain, because I'm not sure what characters are valid in
45  * passwords, or what tools people are likely to use to cut and paste
46  * them.  It seems prudent not to introduce control or meta chars,
47  * unless the user introduced them first.  And having the control
48  * chars all map to themselves insures that newline and
49  * carriage-return are safely handled.
50  */
51
52 static unsigned char
53 shifts[] = {
54     0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15,
55    16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
56   114,120, 53, 79, 96,109, 72,108, 70, 64, 76, 67,116, 74, 68, 87,
57   111, 52, 75,119, 49, 34, 82, 81, 95, 65,112, 86,118,110,122,105,
58    41, 57, 83, 43, 46,102, 40, 89, 38,103, 45, 50, 42,123, 91, 35,
59   125, 55, 54, 66,124,126, 59, 47, 92, 71,115, 78, 88,107,106, 56,
60    36,121,117,104,101,100, 69, 73, 99, 63, 94, 93, 39, 37, 61, 48,
61    58,113, 32, 90, 44, 98, 60, 51, 33, 97, 62, 77, 84, 80, 85,223,
62   225,216,187,166,229,189,222,188,141,249,148,200,184,136,248,190,
63   199,170,181,204,138,232,218,183,255,234,220,247,213,203,226,193,
64   174,172,228,252,217,201,131,230,197,211,145,238,161,179,160,212,
65   207,221,254,173,202,146,224,151,140,196,205,130,135,133,143,246,
66   192,159,244,239,185,168,215,144,139,165,180,157,147,186,214,176,
67   227,231,219,169,175,156,206,198,129,164,150,210,154,177,134,127,
68   182,128,158,208,162,132,167,209,149,241,153,251,237,236,171,195,
69   243,233,253,240,194,250,191,155,142,137,245,235,163,242,178,152 };
70
71
72 /* SCRAMBLE and DESCRAMBLE work like this:
73  *
74  * scramble(STR) returns SCRM, a scrambled copy of STR.  SCRM[0] is a
75  * single letter indicating the scrambling method.  As of this
76  * writing, the only valid method is 'A', but check the code for more
77  * up-to-date information.  The copy will have been allocated with
78  * xmalloc(). 
79  *
80  * descramble(SCRM) returns STR, again in its own xmalloc'd space.
81  * descramble() uses SCRM[0] to determine which method of unscrambling
82  * to use.  If it does not recognize the method, it dies with error.
83  */
84
85 /* Return a xmalloc'd, scrambled version of STR. */
86 char *
87 scramble (char *str)
88 {
89     int i;
90     char *s;
91
92     /* +2 to hold the 'A' prefix that indicates which version of
93        scrambling this is (the first, obviously, since we only do one
94        kind of scrambling so far), and then the '\0' of course.  */
95     s = (char *) xmalloc (strlen (str) + 2);
96
97     /* Scramble (TM) version prefix. */
98     s[0] = 'A';
99     strcpy (s + 1, str);
100
101     for (i = 1; s[i]; i++)
102         s[i] = shifts[(unsigned char)(s[i])];
103
104     return s;
105 }
106
107 /* Decode the string in place. */
108 char *
109 descramble (char *str)
110 {
111     char *s;
112     int i;
113
114     /* For now we can only handle one kind of scrambling.  In the future
115        there may be other kinds, and this `if' will become a `switch'.  */
116     if (str[0] != 'A')
117 #ifndef DIAGNOSTIC
118         error (1, 0, "descramble: unknown scrambling method");
119 #else  /* DIAGNOSTIC */
120     {
121         fprintf (stderr, "descramble: unknown scrambling method\n", str);
122         fflush (stderr);
123         exit (EXIT_FAILURE);
124     }
125 #endif  /* DIAGNOSTIC */
126
127     /* Method `A' is symmetrical, so scramble again to decrypt. */
128     s = scramble (str + 1);
129
130     /* Shift the whole string one char to the left, pushing the unwanted
131        'A' off the left end.  Safe, because s is null-terminated. */
132     for (i = 0; s[i]; i++)
133         s[i] = s[i + 1];
134
135     return s;
136 }
137
138 #endif /* (AUTH_CLIENT_SUPPORT || AUTH_SERVER_SUPPORT) from top of file */
139
140 #ifdef DIAGNOSTIC
141 int
142 main( int argc, char **argv )
143 {
144     int i;
145     char *e, *m, biggie[256];
146
147     char *cleartexts[5];
148     cleartexts[0] = "first";
149     cleartexts[1] = "the second";
150     cleartexts[2] = "this is the third";
151     cleartexts[3] = "$#% !!\\3";
152     cleartexts[4] = biggie;
153
154     /* Set up the most important test string: */
155     /* Can't have a real ASCII zero in the string, because we want to
156        use printf, so we substitute the character zero. */
157     biggie[0] = '0';
158     /* The rest of the string gets straight ascending ASCII. */
159     for (i = 1; i < 256; i++)
160         biggie[i] = i;
161
162     /* Test all the strings. */
163     for (i = 0; i < 5; i++)
164     {
165         printf ("clear%d: %s\n", i, cleartexts[i]);
166         e = scramble (cleartexts[i]);
167         printf ("scram%d: %s\n", i, e);
168         m = descramble (e);
169         free (e);
170         printf ("clear%d: %s\n\n", i, m);
171         free (m);
172     }
173
174     fflush (stdout);
175     return 0;
176 }
177 #endif /* DIAGNOSTIC */
178
179 /*
180  * ;;; The Emacs Lisp that did the dirty work ;;;
181  * (progn
182  * 
183  *   ;; Helper func.
184  *   (defun random-elt (lst)
185  *     (let* ((len (length lst))
186  *            (rnd (random len)))
187  *       (nth rnd lst)))
188  * 
189  *   ;; A list of all characters under 127, each appearing once.
190  *   (setq non-meta-chars
191  *         (let ((i 0)
192  *               (l nil))
193  *           (while (< i 127)
194  *             (setq l (cons i l) 
195  *                   i (1+ i)))
196  *           l))
197  * 
198  *   ;; A list of all characters 127 and above, each appearing once.
199  *   (setq meta-chars
200  *         (let ((i 127)
201  *               (l nil))
202  *           (while (< i 256)
203  *             (setq l (cons i l) 
204  *                   i (1+ i)))
205  *           l))
206  * 
207  *   ;; A vector that will hold the chars in a random order.
208  *   (setq scrambled-chars (make-vector 256 0))
209  * 
210  *   ;; These characters should map to themselves.
211  *   (let ((i 0))
212  *     (while (< i 32)
213  *       (aset scrambled-chars i i)
214  *       (setq non-meta-chars (delete i non-meta-chars) 
215  *             i (1+ i))))
216  *   
217  *   ;; Assign random (but unique) values, within the non-meta chars. 
218  *   (let ((i 32))
219  *     (while (< i 127)
220  *       (let ((ch (random-elt non-meta-chars)))
221  *         (if (= 0 (aref scrambled-chars i))
222  *             (progn
223  *               (aset scrambled-chars i ch)
224  *               (aset scrambled-chars ch i)
225  *               (setq non-meta-chars (delete ch non-meta-chars)
226  *                     non-meta-chars (delete i non-meta-chars))))
227  *         (setq i (1+ i)))))
228  * 
229  *   ;; Assign random (but unique) values, within the non-meta chars. 
230  *   (let ((i 127))
231  *     (while (< i 256)
232  *       (let ((ch (random-elt meta-chars)))
233  *         (if (= 0 (aref scrambled-chars i))
234  *             (progn
235  *               (aset scrambled-chars i ch)
236  *               (aset scrambled-chars ch i)
237  *               (setq meta-chars (delete ch meta-chars)
238  *                     meta-chars (delete i meta-chars))))
239  *         (setq i (1+ i)))))
240  * 
241  *   ;; Now use the `scrambled-chars' vector to get your C array.
242  *   )
243  */