root/branches/branch-5-0/mapserver/mapcrypto.c

Revision 6373, 20.0 kB (checked in by dmorissette, 1 year ago)

Renamed map.h to mapserver.h (ticket #1437)

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
Line 
1 /******************************************************************************
2  *
3  * Project:  MapServer
4  * Purpose:  Encryption functions (see MS-RFC-18)
5  * Author:   Daniel Morissette
6  *
7  ******************************************************************************
8  * Copyright (c) 1996-2006 Regents of the University of Minnesota.
9  *
10  * Permission is hereby granted, free of charge, to any person obtaining a
11  * copy of this software and associated documentation files (the "Software"),
12  * to deal in the Software without restriction, including without limitation
13  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
14  * and/or sell copies of the Software, and to permit persons to whom the
15  * Software is furnished to do so, subject to the following conditions:
16  *
17  * The above copyright notice and this permission notice shall be included in
18  * all copies of this Software or works derived from this Software.
19  *
20  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
21  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
23  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
25  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
26  * DEALINGS IN THE SOFTWARE.
27  ****************************************************************************/
28
29 #include <assert.h>
30 #include <ctype.h>    /* isxdigit() */
31 #include <stdlib.h>   /* rand() */
32 #include <time.h>     /* time() */
33
34 #include "mapserver.h"
35
36 MS_CVSID("$Id$")
37
38
39 /**********************************************************************
40  * encipher() and decipher() from the Tiny Encryption Algorithm (TEA)
41  * website at:
42  *   http://www.simonshepherd.supanet.com/tea.htm
43  *
44  * TEA was developed and placed in the public domain by David Wheeler
45  * and Roger Needham at the Computer Laboratory of Cambridge University.
46  *
47  * The source below came with the following public domain notice:
48  *
49  *   "Please feel free to use any of this code in your applications.
50  *    The TEA algorithm (including new-variant TEA) has been placed
51  *    in the public domain, as have my assembly language implementations."
52  *
53  * ... and the following usage notes:
54  *
55  * All the routines have the general form
56  *
57  *  void encipher(const unsigned long *const v,unsigned long *const w,
58  *                const unsigned long * const k);
59  *
60  *  void decipher(const unsigned long *const v,unsigned long *const w,
61  *                const unsigned long * const k);
62  *
63  * TEA takes 64 bits of data in v[0] and v[1], and 128 bits of key in
64  * k[0] - k[3]. The result is returned in w[0] and w[1]. Returning the
65  * result separately makes implementation of cipher modes other than
66  * Electronic Code Book a little bit easier.
67  *
68  * TEA can be operated in any of the modes of DES.
69  *
70  * n is the number of iterations. 32 is ample, 16 is sufficient, as few
71  * as eight should be OK for most applications, especially ones where
72  * the data age quickly (real-time video, for example). The algorithm
73  * achieves good dispersion after six iterations. The iteration count
74  * can be made variable if required.
75  *
76  * Note this algorithm is optimised for 32-bit CPUs with fast shift
77  * capabilities. It can very easily be ported to assembly language
78  * on most CPUs.
79  *
80  * delta is chosen to be the Golden ratio ((5/4)1/2 - 1/2 ~ 0.618034)
81  * multiplied by 232. On entry to decipher(), sum is set to be delta * n.
82  * Which way round you call the functions is arbitrary: DK(EK(P)) = EK(DK(P))
83  * where EK and DK are encryption and decryption under key K respectively.
84  *
85  **********************************************************************/
86
87 static void encipher(const ms_uint32 *const v, ms_uint32 *const w,
88                      const ms_uint32 *const k)
89 {
90    register ms_uint32   y=v[0],z=v[1],sum=0,delta=0x9E3779B9,n=32;
91
92    while(n-->0)
93    {
94       y += ((z << 4 ^ z >> 5) + z) ^ (sum + k[sum&3]);
95       sum += delta;
96       z += ((y << 4 ^ y >> 5) + y) ^ (sum + k[sum>>11 & 3]);
97    }
98
99    w[0]=y; w[1]=z;
100 }
101
102 static void decipher(const ms_uint32 *const v, ms_uint32 *const w,
103                      const ms_uint32 *const k)
104 {
105    register ms_uint32       y=v[0],z=v[1],sum=0xC6EF3720, delta=0x9E3779B9,n=32;
106
107    /* sum = delta<<5, in general sum = delta * n */
108
109    while(n-->0)
110    {
111       z -= ((y << 4 ^ y >> 5) + y) ^ (sum + k[sum>>11 & 3]);
112       sum -= delta;
113       y -= ((z << 4 ^ z >> 5) + z) ^ (sum + k[sum&3]);
114    }
115    
116    w[0]=y; w[1]=z;
117 }
118
119 /**********************************************************************
120  *                          msHexEncode()
121  *
122  * Hex-encode numbytes from in[] and return the result in out[].
123  *
124  * out[] should be preallocated by the caller to be at least 2*numbytes+1
125  * (+1 for the terminating '\0')
126  **********************************************************************/
127 void msHexEncode(const unsigned char *in, char *out, int numbytes)
128 {
129     char *hex = "0123456789ABCDEF";
130
131     while (numbytes-- > 0)
132     {
133         *out++ = hex[*in/16];
134         *out++ = hex[*in%16];
135         in++;
136     }
137     *out = '\0';
138 }
139
140 /**********************************************************************
141  *                          msHexDecode()
142  *
143  * Hex-decode numchars from in[] and return the result in out[].
144  *
145  * If numchars > 0 then only up to this number of chars from in[] are
146  * processed, otherwise the full in[] string up to the '\0' is processed.
147  *
148  * out[] should be preallocated by the caller to be large enough to hold
149  * the resulting bytes.
150  *
151  * Returns the number of bytes written to out[] which may be different from
152  * numchars/2 if an error or a '\0' is encountered.
153  **********************************************************************/
154 int msHexDecode(const char *in, unsigned char *out, int numchars)
155 {
156     int numbytes_out = 0;
157
158     /* Make sure numchars is even */
159     numchars = (numchars/2) * 2;
160
161     if (numchars < 2)
162         numchars = -1; /* Will result in this value being ignored in the loop*/
163
164     while (*in != '\0' && *(in+1) != '\0' && numchars != 0)
165     {
166         *out = 0x10 * (*in >= 'A' ? ((*in & 0xdf) - 'A')+10 : (*in - '0'));
167         in++;
168         *out += (*in >= 'A' ? ((*in & 0xdf) - 'A')+10 : (*in - '0'));
169         in++;
170
171         out++;
172         numbytes_out++;
173
174         numchars -= 2;
175     }
176
177     return numbytes_out;
178 }
179
180
181 /**********************************************************************
182  *                       msGenerateEncryptionKey()
183  *
184  * Create a new encryption key.
185  *
186  * The output buffer should be at least MS_ENCRYPTION_KEY_SIZE bytes.
187  **********************************************************************/
188
189 int msGenerateEncryptionKey(unsigned char *k)
190 {
191     int i;
192
193     /* Use current time as seed for rand() */
194     srand( (unsigned int) time( NULL ));
195
196     for(i=0; i<MS_ENCRYPTION_KEY_SIZE; i++)
197         k[i] = (unsigned char)rand();
198
199     return MS_SUCCESS;
200 }
201
202 /**********************************************************************
203  *                       msReadEncryptionKeyFromFile()
204  *
205  * Read and decode hex-encoded encryption key from file and returns the
206  * key in the 'unsigned char k[MS_ENCRYPTION_KEY_SIZE]' buffer that is
207  * provided by the caller.
208  *
209  * Returns MS_SUCCESS/MS_FAILURE.
210  **********************************************************************/
211
212 int msReadEncryptionKeyFromFile(const char *keyfile, unsigned char *k)
213 {
214     FILE *fp;
215     char szBuf[100];
216     int numchars;
217
218     if ((fp = fopen(keyfile, "rt")) == NULL)
219     {
220         msSetError(MS_MISCERR, "Cannot open key file.",
221                    "msReadEncryptionKeyFromFile()");
222         return MS_FAILURE;
223     }
224
225     numchars = fread(szBuf, sizeof(unsigned char), MS_ENCRYPTION_KEY_SIZE*2, fp);
226     fclose(fp);
227     szBuf[MS_ENCRYPTION_KEY_SIZE*2] = '\0';
228
229     if (numchars != MS_ENCRYPTION_KEY_SIZE*2)
230     {
231         msSetError(MS_MISCERR, "Invalid key file, got %d chars, expected %d.",
232                    "msReadEncryptionKeyFromFile()",
233                    numchars, MS_ENCRYPTION_KEY_SIZE*2);
234         return MS_FAILURE;
235     }
236
237     msHexDecode(szBuf, k, MS_ENCRYPTION_KEY_SIZE*2);
238
239     return MS_SUCCESS;
240 }
241
242 /**********************************************************************
243  *                       msLoadEncryptionKey()
244  *
245  * Load and decode hex-encoded encryption key from file and returns the
246  * key in the 'unsigned char k[MS_ENCRYPTION_KEY_SIZE]' buffer that is
247  * provided by the caller.
248  *
249  * The first time that msLoadEncryptionKey() is called for a given mapObj
250  * it will load the encryption key and cache it in mapObj->encryption_key.
251  * If the key is already set in the mapObj then it does nothing and returns.
252  *
253  * The location of the encryption key can be specified in two ways,
254  * either by setting the environment variable MS_ENCRYPTION_KEY or using
255  * a CONFIG directive:
256  *    CONFIG MS_ENCRYPTION_KEY "/path/to/mykey.txt"
257  * Returns MS_SUCCESS/MS_FAILURE.
258  **********************************************************************/
259
260 static int msLoadEncryptionKey(mapObj *map)
261 {
262     const char *keyfile;
263
264     if (map == NULL)
265     {
266         msSetError(MS_MISCERR, "NULL MapObj.", "msLoadEncryptionKey()");
267         return MS_FAILURE;
268     }
269
270     if (map->encryption_key_loaded)
271         return MS_SUCCESS;  /* Already loaded */
272
273     keyfile = msGetConfigOption(map, "MS_ENCRYPTION_KEY");
274
275     if (keyfile == NULL)
276         keyfile = getenv("MS_ENCRYPTION_KEY");
277
278     if (keyfile &&
279         msReadEncryptionKeyFromFile(keyfile,map->encryption_key) == MS_SUCCESS)
280     {
281         map->encryption_key_loaded = MS_TRUE;
282     }
283     else
284     {
285         msSetError(MS_MISCERR, "Failed reading encryption key. Make sure "
286                    "MS_ENCRYPTION_KEY is set and points to a valid key file.",
287                    "msLoadEncryptionKey()");
288         return MS_FAILURE;
289     }
290
291     return MS_SUCCESS;
292 }
293
294 /**********************************************************************
295  *                        msEncryptStringWithKey()
296  *
297  * Encrypts and hex-encodes the contents of string in[] and returns the
298  * result in out[] which should have been pre-allocated by the caller
299  * to be at least twice the size of in[] + 16+1 bytes (for padding + '\0').
300  *
301  **********************************************************************/
302
303 void msEncryptStringWithKey(const unsigned char *key, const char *in, char *out)
304 {
305     ms_uint32 v[4], w[4];
306     const ms_uint32 *k;
307     int last_block = MS_FALSE;
308    
309     /* Casting the key this way is safe only as long as longs are 4 bytes
310      * on this platform */
311     assert(sizeof(ms_uint32) == 4);
312     k = (const ms_uint32 *) key;
313    
314     while(!last_block)
315     {
316         int i, j;
317         /* encipher() takes v[2] (64 bits) as input.
318          * Copy bytes from in[] to the v[2] input array (pair of 4 bytes)
319          * v[] is padded with zeros if string doesn't align with 8 bytes
320          */
321         v[0] = 0;
322         v[1] = 0;
323         for(i=0; !last_block && i<2; i++)
324         {
325             for(j=0; j<4; j++)
326             {
327                 if (*in == '\0')
328                 {
329                     last_block = MS_TRUE;
330                     break;
331                 }
332
333                 v[i] |= *in << (j*8);
334                 in++;
335             }
336         }
337
338         if (*in == '\0')
339             last_block = MS_TRUE;
340
341         /* Do the actual encryption */
342         encipher(v, w, k);
343
344         /* Append hex-encoded bytes to output, 4 bytes at a time */
345         msHexEncode((unsigned char *)w, out, 4);
346         out += 8;
347         msHexEncode((unsigned char *)(w+1), out, 4);
348         out += 8;
349
350     }
351
352     /* Make sure output is 0-terminated */
353     *out = '\0';
354 }
355
356 /**********************************************************************
357  *                        msDecryptStringWithKey()
358  *
359  * Hex-decodes and then decrypts the contents of string in[] and returns the
360  * result in out[] which should have been pre-allocated by the caller
361  * to be at least half the size of in[].
362  *
363  **********************************************************************/
364
365 void msDecryptStringWithKey(const unsigned char *key, const char *in, char *out)
366 {
367     ms_uint32 v[4], w[4];
368     const ms_uint32 *k;
369     int last_block = MS_FALSE;
370    
371     /* Casting the key this way is safe only as long as longs are 4 bytes
372      * on this platform */
373     assert(sizeof(ms_uint32) == 4);
374     k = (const ms_uint32 *) key;
375    
376     while(!last_block)
377     {
378         int i;
379         /* decipher() takes v[2] (64 bits) as input.
380          * Copy bytes from in[] to the v[2] input array (pair of 4 bytes)
381          * v[] is padded with zeros if string doesn't align with 8 bytes
382          */
383         v[0] = 0;
384         v[1] = 0;
385
386         if (msHexDecode(in, (unsigned char *)v, 8) != 4)
387             last_block = MS_TRUE;
388         else
389         {
390             in += 8;
391             if (msHexDecode(in, (unsigned char *)(v+1), 8) != 4)
392                 last_block = MS_TRUE;
393             else
394                 in += 8;
395         }
396
397         /* Do the actual decryption */
398         decipher(v, w, k);
399
400         /* Copy the results to out[] */
401         for(i=0; i<2; i++)
402         {
403             *out++ = (w[i] & 0x000000ff);
404             *out++ = (w[i] & 0x0000ff00) >> 8;
405             *out++ = (w[i] & 0x00ff0000) >> 16;
406             *out++ = (w[i] & 0xff000000) >> 24;
407         }
408
409         if (*in == '\0')
410             last_block = MS_TRUE;
411     }
412
413     /* Make sure output is 0-terminated */
414     *out = '\0';
415 }
416
417 /**********************************************************************
418  *                        msDecryptStringTokens()
419  *
420  * Returns a newly allocated string (to be msFree'd by the caller) in
421  * which all occurences of encrypted strings delimited by {...} have
422  * been decrypted.
423  *
424  **********************************************************************/
425
426 char *msDecryptStringTokens(mapObj *map, const char *in)
427 {
428     char *outbuf, *out;
429
430     if (map == NULL)
431     {
432         msSetError(MS_MISCERR, "NULL MapObj.", "msLoadEncryptionKey()");
433         return NULL;
434     }
435
436     /* Start with a copy of the string. Decryption can only result in
437      * a string with the same or shorter length */
438     if ((outbuf = (char *)malloc((strlen(in)+1)*sizeof(char))) == NULL)
439     {
440         msSetError(MS_MEMERR, NULL, "msDecryptStringTokens()");
441         return NULL;
442     }
443     out = outbuf;
444
445     while(*in != '\0')
446     {
447         if (*in == '{')
448         {
449             /* Possibly beginning of a token, look for closing bracket
450             ** and make sure all chars in between are valid hex encoding chars
451             */
452             const char *pszStart, *pszEnd;
453             int valid_token = MS_FALSE;
454
455             pszStart = in+1;
456             if ( (pszEnd = strchr(pszStart, '}')) != NULL &&
457                  pszEnd - pszStart > 1)
458             {
459                 const char *pszTmp;
460                 valid_token = MS_TRUE;
461                 for(pszTmp = pszStart; pszTmp < pszEnd; pszTmp++)
462                 {
463                     if (!isxdigit(*pszTmp))
464                     {
465                         valid_token = MS_FALSE;
466                         break;
467                     }
468                 }
469             }
470
471             if (valid_token)
472             {
473                 /* Go ahead and decrypt the token */
474                 char *pszTmp;
475
476                 /* Make sure encryption key is loaded. We do this here instead
477                  * of at the beginning of the function to avoid loading the
478                  * key unless ready necessary. This is a very cheap call if
479                  * the key is already loaded
480                  */
481                 if (msLoadEncryptionKey(map) != MS_SUCCESS)
482                     return NULL;
483
484                 pszTmp = (char*)malloc( (pszEnd-pszStart+1)*sizeof(char));
485                 strncpy(pszTmp, pszStart, pszEnd-pszStart);
486                 pszTmp[pszEnd-pszStart] = '\0';
487
488                 msDecryptStringWithKey(map->encryption_key, pszTmp, out);
489
490                 out += strlen(out);
491                 in = pszEnd+1;
492             }
493             else
494             {
495                 /* Not a valid token, just copy the '{' and keep going */
496                 *out++ = *in++;
497             }
498         }
499         else
500         {
501             /* Just copy any other chars */
502             *out++ = *in++;
503         }
504     }
505     *out = '\0';
506
507     return outbuf;
508 }
509
510
511 #ifdef TEST_MAPCRYPTO
512
513 /* Test for mapcrypto.c functions. To run these tests, use the following
514 ** Makefile directive:
515
516 test_mapcrypto: $(LIBMAP_STATIC) mapcrypto.c
517         $(CC) $(CFLAGS) mapcrypto.c -DTEST_MAPCRYPTO $(EXE_LDFLAGS) -o test_mapcrypto
518
519 **
520 */
521 int main(int argc, char *argv[])
522 {
523     const unsigned char bytes_in[] = {0x12, 0x34, 0xff, 0x00, 0x44, 0x22};
524     unsigned char bytes_out[8], encryption_key[MS_ENCRYPTION_KEY_SIZE*2+1];
525     char string_buf[256], string_buf2[256];
526     int numbytes = 0;
527
528     /*
529     ** Test msHexEncode()
530     */
531     msHexEncode(bytes_in, string_buf, 6);
532     printf("msHexEncode returned '%s'\n", string_buf);
533
534     /*
535     ** Test msHexDecode()
536     */
537     memset(bytes_out, 0, 8);
538     numbytes = msHexDecode(string_buf, bytes_out, -1);
539     printf("msHexDecode(%s, -1) = %d, bytes_out = %x, %x, %x, %x, %x, %x, %x, %x\n",
540            string_buf, numbytes,
541            bytes_out[0], bytes_out[1], bytes_out[2], bytes_out[3],
542            bytes_out[4], bytes_out[5], bytes_out[6], bytes_out[7]);
543
544     memset(bytes_out, 0, 8);
545     numbytes = msHexDecode(string_buf, bytes_out, 4);
546     printf("msHexDecode(%s, 4) = %d, bytes_out = %x, %x, %x, %x, %x, %x, %x, %x\n",
547            string_buf, numbytes,
548            bytes_out[0], bytes_out[1], bytes_out[2], bytes_out[3],
549            bytes_out[4], bytes_out[5], bytes_out[6], bytes_out[7]);
550
551     memset(bytes_out, 0, 8);
552     numbytes = msHexDecode(string_buf, bytes_out, 20);
553     printf("msHexDecode(%s, 20) = %d, bytes_out = %x, %x, %x, %x, %x, %x, %x, %x\n",
554            string_buf, numbytes,
555            bytes_out[0], bytes_out[1], bytes_out[2], bytes_out[3],
556            bytes_out[4], bytes_out[5], bytes_out[6], bytes_out[7]);
557
558     /*
559     ** Test loading encryption key
560     */
561     if (msReadEncryptionKeyFromFile("/tmp/test.key", encryption_key) != MS_SUCCESS)
562     {
563         printf("msReadEncryptionKeyFromFile() = MS_FAILURE\n");
564         printf("Aborting tests!\n");
565         msWriteError(stderr);
566         return -1;
567     }
568     else
569     {
570         msHexEncode(encryption_key, string_buf, MS_ENCRYPTION_KEY_SIZE);
571         printf("msReadEncryptionKeyFromFile() returned '%s'\n", string_buf);
572     }
573
574     /*
575     ** Test Encryption/Decryption
576     */
577
578     /* First with an 8 bytes input string (test boundaries) */
579     msEncryptStringWithKey(encryption_key, "test1234", string_buf);
580     printf("msEncryptStringWithKey('test1234') returned '%s'\n", string_buf);
581
582     msDecryptStringWithKey(encryption_key, string_buf, string_buf2);
583     printf("msDecryptStringWithKey('%s') returned '%s'\n", string_buf, string_buf2);
584
585     /* Next with an 1 byte input string */
586     msEncryptStringWithKey(encryption_key, "t", string_buf);
587     printf("msEncryptStringWithKey('t') returned '%s'\n", string_buf);
588
589     msDecryptStringWithKey(encryption_key, string_buf, string_buf2);
590     printf("msDecryptStringWithKey('%s') returned '%s'\n", string_buf, string_buf2);
591
592     /* Next with an 12 bytes input string */
593     msEncryptStringWithKey(encryption_key, "test123456", string_buf);
594     printf("msEncryptStringWithKey('test123456') returned '%s'\n", string_buf);
595
596     msDecryptStringWithKey(encryption_key, string_buf, string_buf2);
597     printf("msDecryptStringWithKey('%s') returned '%s'\n", string_buf, string_buf2);
598
599     /*
600     ** Test decryption with tokens
601     */
602     {
603         char *pszBuf;
604         mapObj *map;
605         //map = msNewMapObj();
606         map = msLoadMap("/tmp/test.map", NULL);
607
608         sprintf(string_buf2, "string with a {%s} encrypted token", string_buf);
609    
610         pszBuf = msDecryptStringTokens(map, string_buf2);
611         if (pszBuf == NULL)
612         {
613             printf("msDecryptStringTokens() failed.\n");
614             printf("Aborting tests!\n");
615             msWriteError(stderr);
616             return -1;
617         }
618         else
619         {
620             printf("msDecryptStringTokens('%s') returned '%s'\n",
621                    string_buf2, pszBuf);
622         }
623         msFree(pszBuf);
624         msFreeMap(map);
625     }
626
627     return 0;
628 }
629
630
631 #endif /* TEST_MAPCRYPTO */
Note: See TracBrowser for help on using the browser.