root/trunk/mapserver/mapcrypto.c

Revision 7544, 20.0 kB (checked in by pramsey, 7 months ago)

Remove C++-style comments and most other warnings thrown by -pedantic (#2598)

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
Line 
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 MS_CVSID("$Id$")
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
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    {
95       y += ((z << 4 ^ z >> 5) + z) ^ (sum + k[sum&3]);
96       sum += delta;
97       z += ((y << 4 ^ y >> 5) + y) ^ (sum + k[sum>>11 & 3]);
98    }
99
100    w[0]=y; w[1]=z;
101 }
102
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    {
112       z -= ((y << 4 ^ y >> 5) + y) ^ (sum + k[sum>>11 & 3]);
113       sum -= delta;
114       y -= ((z << 4 ^ z >> 5) + z) ^ (sum + k[sum&3]);
115    }
116    
117    w[0]=y; 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  **********************************************************************/
128 void msHexEncode(const unsigned char *in, char *out, int numbytes)
129 {
130     char *hex = "0123456789ABCDEF";
131
132     while (numbytes-- > 0)
133     {
134         *out++ = hex[*in/16];
135         *out++ = hex[*in%16];
136         in++;
137     }
138     *out = '\0';
139 }
140
141 /**********************************************************************
142  *                          msHexDecode()
143  *
144  * Hex-decode numchars from in[] and return the result in out[].
145  *
146  * If numchars > 0 then only up to this number of chars from in[] are
147  * processed, otherwise the full in[] string up to the '\0' is processed.
148  *
149  * out[] should be preallocated by the caller to be large enough to hold
150  * the resulting bytes.
151  *
152  * Returns the number of bytes written to out[] which may be different from
153  * numchars/2 if an error or a '\0' is encountered.
154  **********************************************************************/
155 int msHexDecode(const char *in, unsigned char *out, int numchars)
156 {
157     int numbytes_out = 0;
158
159     /* Make sure numchars is even */
160     numchars = (numchars/2) * 2;
161
162     if (numchars < 2)
163         numchars = -1; /* Will result in this value being ignored in the loop*/
164
165     while (*in != '\0' && *(in+1) != '\0' && numchars != 0)
166     {
167         *out = 0x10 * (*in >= 'A' ? ((*in & 0xdf) - 'A')+10 : (*in - '0'));
168         in++;
169         *out += (*in >= 'A' ? ((*in & 0xdf) - 'A')+10 : (*in - '0'));
170         in++;
171
172         out++;
173         numbytes_out++;
174
175         numchars -= 2;
176     }
177
178     return numbytes_out;
179 }
180
181
182 /**********************************************************************
183  *                       msGenerateEncryptionKey()
184  *
185  * Create a new encryption key.
186  *
187  * The output buffer should be at least MS_ENCRYPTION_KEY_SIZE bytes.
188  **********************************************************************/
189
190 int msGenerateEncryptionKey(unsigned char *k)
191 {
192     int i;
193
194     /* Use current time as seed for rand() */
195     srand( (unsigned int) time( NULL ));
196
197     for(i=0; i<MS_ENCRYPTION_KEY_SIZE; i++)
198         k[i] = (unsigned char)rand();
199
200     return MS_SUCCESS;
201 }
202
203 /**********************************************************************
204  *                       msReadEncryptionKeyFromFile()
205  *
206  * Read and decode hex-encoded encryption key from file and returns the
207  * key in the 'unsigned char k[MS_ENCRYPTION_KEY_SIZE]' buffer that is
208  * provided by the caller.
209  *
210  * Returns MS_SUCCESS/MS_FAILURE.
211  **********************************************************************/
212
213 int msReadEncryptionKeyFromFile(const char *keyfile, unsigned char *k)
214 {
215     FILE *fp;
216     char szBuf[100];
217     int numchars;
218
219     if ((fp = fopen(keyfile, "rt")) == NULL)
220     {
221         msSetError(MS_MISCERR, "Cannot open key file.",
222                    "msReadEncryptionKeyFromFile()");
223         return MS_FAILURE;
224     }
225
226     numchars = fread(szBuf, sizeof(unsigned char), MS_ENCRYPTION_KEY_SIZE*2, fp);
227     fclose(fp);
228     szBuf[MS_ENCRYPTION_KEY_SIZE*2] = '\0';
229
230     if (numchars != MS_ENCRYPTION_KEY_SIZE*2)
231     {
232         msSetError(MS_MISCERR, "Invalid key file, got %d chars, expected %d.",
233                    "msReadEncryptionKeyFromFile()",
234                    numchars, MS_ENCRYPTION_KEY_SIZE*2);
235         return MS_FAILURE;
236     }
237
238     msHexDecode(szBuf, k, MS_ENCRYPTION_KEY_SIZE*2);
239
240     return MS_SUCCESS;
241 }
242
243 /**********************************************************************
244  *                       msLoadEncryptionKey()
245  *
246  * Load and decode hex-encoded encryption key from file and returns the
247  * key in the 'unsigned char k[MS_ENCRYPTION_KEY_SIZE]' buffer that is
248  * provided by the caller.
249  *
250  * The first time that msLoadEncryptionKey() is called for a given mapObj
251  * it will load the encryption key and cache it in mapObj->encryption_key.
252  * If the key is already set in the mapObj then it does nothing and returns.
253  *
254  * The location of the encryption key can be specified in two ways,
255  * either by setting the environment variable MS_ENCRYPTION_KEY or using
256  * a CONFIG directive:
257  *    CONFIG MS_ENCRYPTION_KEY "/path/to/mykey.txt"
258  * Returns MS_SUCCESS/MS_FAILURE.
259  **********************************************************************/
260
261 static int msLoadEncryptionKey(mapObj *map)
262 {
263     const char *keyfile;
264
265     if (map == NULL)
266     {
267         msSetError(MS_MISCERR, "NULL MapObj.", "msLoadEncryptionKey()");
268         return MS_FAILURE;
269     }
270
271     if (map->encryption_key_loaded)
272         return MS_SUCCESS;  /* Already loaded */
273
274     keyfile = msGetConfigOption(map, "MS_ENCRYPTION_KEY");
275
276     if (keyfile == NULL)
277         keyfile = getenv("MS_ENCRYPTION_KEY");
278
279     if (keyfile &&
280         msReadEncryptionKeyFromFile(keyfile,map->encryption_key) == MS_SUCCESS)
281     {
282         map->encryption_key_loaded = MS_TRUE;
283     }
284     else
285     {
286         msSetError(MS_MISCERR, "Failed reading encryption key. Make sure "
287                    "MS_ENCRYPTION_KEY is set and points to a valid key file.",
288                    "msLoadEncryptionKey()");
289         return MS_FAILURE;
290     }
291
292     return MS_SUCCESS;
293 }
294
295 /**********************************************************************
296  *                        msEncryptStringWithKey()
297  *
298  * Encrypts and hex-encodes the contents of string in[] and returns the
299  * result in out[] which should have been pre-allocated by the caller
300  * to be at least twice the size of in[] + 16+1 bytes (for padding + '\0').
301  *
302  **********************************************************************/
303
304 void msEncryptStringWithKey(const unsigned char *key, const char *in, char *out)
305 {
306     ms_uint32 v[4], w[4];
307     const ms_uint32 *k;
308     int last_block = MS_FALSE;
309    
310     /* Casting the key this way is safe only as long as longs are 4 bytes
311      * on this platform */
312     assert(sizeof(ms_uint32) == 4);
313     k = (const ms_uint32 *) key;
314    
315     while(!last_block)
316     {
317         int i, j;
318         /* encipher() takes v[2] (64 bits) as input.
319          * Copy bytes from in[] to the v[2] input array (pair of 4 bytes)
320          * v[] is padded with zeros if string doesn't align with 8 bytes
321          */
322         v[0] = 0;
323         v[1] = 0;
324         for(i=0; !last_block && i<2; i++)
325         {
326             for(j=0; j<4; j++)
327             {
328                 if (*in == '\0')
329                 {
330                     last_block = MS_TRUE;
331                     break;
332                 }
333
334                 v[i] |= *in << (j*8);
335                 in++;
336             }
337         }
338
339         if (*in == '\0')
340             last_block = MS_TRUE;
341
342         /* Do the actual encryption */
343         encipher(v, w, k);
344
345         /* Append hex-encoded bytes to output, 4 bytes at a time */
346         msHexEncode((unsigned char *)w, out, 4);
347         out += 8;
348         msHexEncode((unsigned char *)(w+1), out, 4);
349         out += 8;
350
351     }
352
353     /* Make sure output is 0-terminated */
354     *out = '\0';
355 }
356
357 /**********************************************************************
358  *                        msDecryptStringWithKey()
359  *
360  * Hex-decodes and then decrypts the contents of string in[] and returns the
361  * result in out[] which should have been pre-allocated by the caller
362  * to be at least half the size of in[].
363  *
364  **********************************************************************/
365
366 void msDecryptStringWithKey(const unsigned char *key, const char *in, char *out)
367 {
368     ms_uint32 v[4], w[4];
369     const ms_uint32 *k;
370     int last_block = MS_FALSE;
371    
372     /* Casting the key this way is safe only as long as longs are 4 bytes
373      * on this platform */
374     assert(sizeof(ms_uint32) == 4);
375     k = (const ms_uint32 *) key;
376    
377     while(!last_block)
378     {
379         int i;
380         /* decipher() takes v[2] (64 bits) as input.
381          * Copy bytes from in[] to the v[2] input array (pair of 4 bytes)
382          * v[] is padded with zeros if string doesn't align with 8 bytes
383          */
384         v[0] = 0;
385         v[1] = 0;
386
387         if (msHexDecode(in, (unsigned char *)v, 8) != 4)
388             last_block = MS_TRUE;
389         else
390         {
391             in += 8;
392             if (msHexDecode(in, (unsigned char *)(v+1), 8) != 4)
393                 last_block = MS_TRUE;
394             else
395                 in += 8;
396         }
397
398         /* Do the actual decryption */
399         decipher(v, w, k);
400
401         /* Copy the results to out[] */
402         for(i=0; i<2; i++)
403         {
404             *out++ = (w[i] & 0x000000ff);
405             *out++ = (w[i] & 0x0000ff00) >> 8;
406             *out++ = (w[i] & 0x00ff0000) >> 16;
407             *out++ = (w[i] & 0xff000000) >> 24;
408         }
409
410         if (*in == '\0')
411             last_block = MS_TRUE;
412     }
413
414     /* Make sure output is 0-terminated */
415     *out = '\0';
416 }
417
418 /**********************************************************************
419  *                        msDecryptStringTokens()
420  *
421  * Returns a newly allocated string (to be msFree'd by the caller) in
422  * which all occurences of encrypted strings delimited by {...} have
423  * been decrypted.
424  *
425  **********************************************************************/
426
427 char *msDecryptStringTokens(mapObj *map, const char *in)
428 {
429     char *outbuf, *out;
430
431     if (map == NULL)
432     {
433         msSetError(MS_MISCERR, "NULL MapObj.", "msLoadEncryptionKey()");
434         return NULL;
435     }
436
437     /* Start with a copy of the string. Decryption can only result in
438      * a string with the same or shorter length */
439     if ((outbuf = (char *)malloc((strlen(in)+1)*sizeof(char))) == NULL)
440     {
441         msSetError(MS_MEMERR, NULL, "msDecryptStringTokens()");
442         return NULL;
443     }
444     out = outbuf;
445
446     while(*in != '\0')
447     {
448         if (*in == '{')
449         {
450             /* Possibly beginning of a token, look for closing bracket
451             ** and make sure all chars in between are valid hex encoding chars
452             */
453             const char *pszStart, *pszEnd;
454             int valid_token = MS_FALSE;
455
456             pszStart = in+1;
457             if ( (pszEnd = strchr(pszStart, '}')) != NULL &&
458                  pszEnd - pszStart > 1)
459             {
460                 const char *pszTmp;
461                 valid_token = MS_TRUE;
462                 for(pszTmp = pszStart; pszTmp < pszEnd; pszTmp++)
463                 {
464                     if (!isxdigit(*pszTmp))
465                     {
466                         valid_token = MS_FALSE;
467                         break;
468                     }
469                 }
470             }
471
472             if (valid_token)
473             {
474                 /* Go ahead and decrypt the token */
475                 char *pszTmp;
476
477                 /* Make sure encryption key is loaded. We do this here instead
478                  * of at the beginning of the function to avoid loading the
479                  * key unless ready necessary. This is a very cheap call if
480                  * the key is already loaded
481                  */
482                 if (msLoadEncryptionKey(map) != MS_SUCCESS)
483                     return NULL;
484
485                 pszTmp = (char*)malloc( (pszEnd-pszStart+1)*sizeof(char));
486                 strncpy(pszTmp, pszStart, pszEnd-pszStart);
487                 pszTmp[pszEnd-pszStart] = '\0';
488
489                 msDecryptStringWithKey(map->encryption_key, pszTmp, out);
490
491                 out += strlen(out);
492                 in = pszEnd+1;
493             }
494             else
495             {
496                 /* Not a valid token, just copy the '{' and keep going */
497                 *out++ = *in++;
498             }
499         }
500         else
501         {
502             /* Just copy any other chars */
503             *out++ = *in++;
504         }
505     }
506     *out = '\0';
507
508     return outbuf;
509 }
510
511
512 #ifdef TEST_MAPCRYPTO
513
514 /* Test for mapcrypto.c functions. To run these tests, use the following
515 ** Makefile directive:
516
517 test_mapcrypto: $(LIBMAP_STATIC) mapcrypto.c
518         $(CC) $(CFLAGS) mapcrypto.c -DTEST_MAPCRYPTO $(EXE_LDFLAGS) -o test_mapcrypto
519
520 **
521 */
522 int main(int argc, char *argv[])
523 {
524     const unsigned char bytes_in[] = {0x12, 0x34, 0xff, 0x00, 0x44, 0x22};
525     unsigned char bytes_out[8], encryption_key[MS_ENCRYPTION_KEY_SIZE*2+1];
526     char string_buf[256], string_buf2[256];
527     int numbytes = 0;
528
529     /*
530     ** Test msHexEncode()
531     */
532     msHexEncode(bytes_in, string_buf, 6);
533     printf("msHexEncode returned '%s'\n", string_buf);
534
535     /*
536     ** Test msHexDecode()
537     */
538     memset(bytes_out, 0, 8);
539     numbytes = msHexDecode(string_buf, bytes_out, -1);
540     printf("msHexDecode(%s, -1) = %d, bytes_out = %x, %x, %x, %x, %x, %x, %x, %x\n",
541            string_buf, numbytes,
542            bytes_out[0], bytes_out[1], bytes_out[2], bytes_out[3],
543            bytes_out[4], bytes_out[5], bytes_out[6], bytes_out[7]);
544
545     memset(bytes_out, 0, 8);
546     numbytes = msHexDecode(string_buf, bytes_out, 4);
547     printf("msHexDecode(%s, 4) = %d, bytes_out = %x, %x, %x, %x, %x, %x, %x, %x\n",
548            string_buf, numbytes,
549            bytes_out[0], bytes_out[1], bytes_out[2], bytes_out[3],
550            bytes_out[4], bytes_out[5], bytes_out[6], bytes_out[7]);
551
552     memset(bytes_out, 0, 8);
553     numbytes = msHexDecode(string_buf, bytes_out, 20);
554     printf("msHexDecode(%s, 20) = %d, bytes_out = %x, %x, %x, %x, %x, %x, %x, %x\n",
555            string_buf, numbytes,
556            bytes_out[0], bytes_out[1], bytes_out[2], bytes_out[3],
557            bytes_out[4], bytes_out[5], bytes_out[6], bytes_out[7]);
558
559     /*
560     ** Test loading encryption key
561     */
562     if (msReadEncryptionKeyFromFile("/tmp/test.key", encryption_key) != MS_SUCCESS)
563     {
564         printf("msReadEncryptionKeyFromFile() = MS_FAILURE\n");
565         printf("Aborting tests!\n");
566         msWriteError(stderr);
567         return -1;
568     }
569     else
570     {
571         msHexEncode(encryption_key, string_buf, MS_ENCRYPTION_KEY_SIZE);
572         printf("msReadEncryptionKeyFromFile() returned '%s'\n", string_buf);
573     }
574
575     /*
576     ** Test Encryption/Decryption
577     */
578
579     /* First with an 8 bytes input string (test boundaries) */
580     msEncryptStringWithKey(encryption_key, "test1234", string_buf);
581     printf("msEncryptStringWithKey('test1234') returned '%s'\n", string_buf);
582
583     msDecryptStringWithKey(encryption_key, string_buf, string_buf2);
584     printf("msDecryptStringWithKey('%s') returned '%s'\n", string_buf, string_buf2);
585
586     /* Next with an 1 byte input string */
587     msEncryptStringWithKey(encryption_key, "t", string_buf);
588     printf("msEncryptStringWithKey('t') returned '%s'\n", string_buf);
589
590     msDecryptStringWithKey(encryption_key, string_buf, string_buf2);
591     printf("msDecryptStringWithKey('%s') returned '%s'\n", string_buf, string_buf2);
592
593     /* Next with an 12 bytes input string */
594     msEncryptStringWithKey(encryption_key, "test123456", string_buf);
595     printf("msEncryptStringWithKey('test123456') returned '%s'\n", string_buf);
596
597     msDecryptStringWithKey(encryption_key, string_buf, string_buf2);
598     printf("msDecryptStringWithKey('%s') returned '%s'\n", string_buf, string_buf2);
599
600     /*
601     ** Test decryption with tokens
602     */
603     {
604         char *pszBuf;
605         mapObj *map;
606         /* map = msNewMapObj(); */
607         map = msLoadMap("/tmp/test.map", NULL);
608
609         sprintf(string_buf2, "string with a {%s} encrypted token", string_buf);
610    
611         pszBuf = msDecryptStringTokens(map, string_buf2);
612         if (pszBuf == NULL)
613         {
614             printf("msDecryptStringTokens() failed.\n");
615             printf("Aborting tests!\n");
616             msWriteError(stderr);
617             return -1;
618         }
619         else
620         {
621             printf("msDecryptStringTokens('%s') returned '%s'\n",
622                    string_buf2, pszBuf);
623         }
624         msFree(pszBuf);
625         msFreeMap(map);
626     }
627
628     return 0;
629 }
630
631
632 #endif /* TEST_MAPCRYPTO */
Note: See TracBrowser for help on using the browser.