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