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