1 /*===========================================================================
2 *
3 * PUBLIC DOMAIN NOTICE
4 * National Center for Biotechnology Information
5 *
6 * This software/database is a "United States Government Work" under the
7 * terms of the United States Copyright Act. It was written as part of
8 * the author's official duties as a United States Government employee and
9 * thus cannot be copyrighted. This software/database is freely available
10 * to the public for use. The National Library of Medicine and the U.S.
11 * Government have not placed any restriction on its use or reproduction.
12 *
13 * Although all reasonable efforts have been taken to ensure the accuracy
14 * and reliability of the software and data, the NLM and the U.S.
15 * Government do not and cannot warrant the performance or results that
16 * may be obtained by using this software or data. The NLM and the U.S.
17 * Government disclaim all warranties, express or implied, including
18 * warranties of performance, merchantability or fitness for any particular
19 * purpose.
20 *
21 * Please cite the author in any work or product based on this material.
22 *
23 * ===========================================================================
24 */
25
26 #include <krypto/extern.h>
27 #include <krypto/cipher.h>
28 #include <krypto/ciphermgr.h>
29
30 #include <krypto/wgaencrypt.h>
31
32 #include <kfs/file.h>
33 #include <kfs/countfile.h>
34 #include <kfs/md5.h>
35 #include <kfs/buffile.h>
36 #include <kfs/nullfile.h>
37
38 #include <klib/printf.h>
39 #include <klib/rc.h>
40 #include <klib/text.h>
41 #include <klib/checksum.h>
42 #include <klib/log.h>
43 #include <klib/debug.h>
44
45 #include <sysalloc.h>
46
47 #include <strtol.h>
48
49 #include <time.h>
50 #include <stdlib.h>
51 #include <string.h>
52 #include <assert.h>
53
54 #include <os-native.h> /* strncasecmp on Windows */
55
56 /* retain errors in encryption to be compatible with C++ */
57 #define RETAINED_COMPATIBILTY_WITH_ERROR 1
58
59
60 #define WGA_AES_BITS (256)
61 #define ECB_BYTES (16)
62 #define DEFAULT_BUFF_SIZE (32*1024)
63
64
65 #define DEBUG_STS(msg) DBGMSG(DBG_KRYPTO,DBG_FLAG(DBG_KRYPTO_STS),msg)
66 #define DEBUG_CFG(msg) DBGMSG(DBG_KRYPTO,DBG_FLAG(DBG_KRYPTO_CFG_,msg)
67 #define DEBUG_ENCRYPT(msg) DBGMSG(DBG_KRYPTO,DBG_FLAG(DBG_KRYPTO_ENCRYPT_,msg)
68 #define DEBUG_DECRYPT(msg) DBGMSG(DBG_KRYPTO,DBG_FLAG(DBG_KRYPTO_DECRYPT_,msg)
69
70 static
CalcMD5(void * buffer,size_t size,char hexstr[32])71 void CalcMD5 (void * buffer, size_t size, char hexstr [32])
72 {
73 static const char to_hex[16] = "0123456789abcdef";
74 MD5State state;
75 uint8_t digest [16];
76 uint8_t * bin;
77 char * hex;
78 int ix;
79
80 MD5StateInit (&state);
81 MD5StateAppend (&state, buffer, size);
82 MD5StateFinish (&state, digest);
83
84 bin = digest;
85 hex = hexstr;
86
87 for (ix = 0; ix < sizeof digest; ++ix)
88 {
89 uint8_t upper;
90 uint8_t lower;
91
92 upper = *bin++;
93 lower = upper & 0xF;
94 upper >>= 4;
95
96 *hex++ = to_hex [upper];
97 *hex++ = to_hex[lower];
98 }
99 }
100
101
102 /* ----------------------------------------------------------------------
103 * KWGAEncFileMagic is extracted from
104 * internal/WGA/access_countrol/src/wga/fuse/enc_reader.[ch]pp
105 *
106 * the size 9 is not commented on but appears to be the size of ASCIZ
107 * that is the 'magic' pattern including the NUL.
108 *
109 * There is no version number in the header and that NUL could be turned
110 * into a single byte version.
111 */
112 typedef char KWGAEncFileMagic [9];
113 static const KWGAEncFileMagic ncbi_crypt_magic = "NeCnBcIo";
114
115 /* ----------------------------------------------------------------------
116 * Some values are stored in the encrypted file header using an odd radix 33
117 */
118 #define KWGA_ENC_FILE_HEADER_RADIX (33)
119
120 #if 0
121 static
122 const char KWGAEncFileHeaderAlphabet[] = "0123456789abcdefghijklmnopqrstuvwxyz";
123 #endif
124
125 /* ----------------------------------------------------------------------
126 * KWGAEncFileHeader is extracted from
127 * internal/WGA/access_countrol/src/wga/fuse/enc_reader.[ch]pp
128 *
129 * There is no version number in this header; see the comment about KWGAEncFileMagic
130 */
131 typedef uint8_t FER_ENCODING;
132 enum
133 {
134 fer_encDES,
135 fer_encBLF,
136 fer_encAES
137 };
138
139 typedef struct KWGAEncFileHeader
140 {
141 char magic [9]; /* "NeCnBcIo" -- NUL could be turned into a version */ /* 00 */
142 char block_sz [8]; /* radixx33 encoded: block size in bytes / usually 4096 */ /* 09 */
143 char file_sz [16]; /* radixx33 encoded: total size of unencrypted file */ /* 11 */
144 char mtime [8]; /* radixx33 encoded: 32 bit time_t */ /* 21 */
145 char fer_enc; /* see FER_ENCODING */ /* 29 */
146 char md5_here; /* bool if non zero, the whole file MD5 is below */ /* 2A */
147 char md5 [32]; /* whole file MD5 is ASCII hex */ /* 2B */
148 char md51 [32]; /* first block MD5 */ /* 4B */
149 char reserved [21]; /* 6B */
150 } KWGAEncFileHeader; /* 80 */
151
152 /* Original definition is in internal/WGA/access_countrol/src/wga/fuse/enc_reader.hpp */
153 static const int8_t header_table[sizeof (KWGAEncFileHeader)] =
154 {
155 101, -6, -23, 5, -93, 20, -128, -36, /* 00 */
156 -42, 74, -98, 104, 42, 12, 127, 37, /* 08 */
157 -47, -61, 124, 54, -124, -94, 47, 72, /* 10 */
158 70, 17, -10, 108, 8, 31, 37, -38, /* 18 */
159 104, -6, -117, 79, 115, 89, 33, -93, /* 20 */
160 -47, -105, -87, -38, 90, -45, -59, -46, /* 28 */
161 -96, 106, 15, -87, -110, -101, 106, -117, /* 30 */
162 39, 73, 120, -30, -63, 21, 127, -32, /* 38 */
163 98, -104, -3, -81, -60, -120, 13, -108, /* 40 */
164 -53, 88, 123, 7, 103, 32, -14, -113, /* 48 */
165 -68, -27, 44, 109, -122, -7, 81, -13, /* 50 */
166 64, 42, -88, -37, -1, -19, 66, -105, /* 58 */
167 -75, -108, -5, -121, -86, 47, -120, -18, /* 60 */
168 -69, -29, -68, 124, -53, -104, -28, 42, /* 68 */
169 120, 52, -80, -23, -110, -101, 106, -117, /* 70 */
170 -21, -35, 12, -117, 9, -122, -21, 31 /* 78 */
171 }; /* 80 */
172
173 static const KWGAEncFileHeader header_const =
174 {
175 "NeCnBcIo", /* NUL terminator is significant */
176 {0},{0},{0},/* not constant */
177 fer_encAES, /* constant as DES and Blowfish are not implmented */
178 0,{0},{0}, /* not constant */
179 {0} /* constant */
180 };
181
182 /* the header is obscured by XOR against a predefined pattern */
183 static
KWGAEncFileHeaderDecrypt(KWGAEncFileHeader * header)184 void KWGAEncFileHeaderDecrypt (KWGAEncFileHeader * header)
185 {
186 size_t ix;
187 int8_t * p = (int8_t*)header;
188
189 assert (header);
190 for (ix = 0; ix < sizeof (*header); ++ix)
191 p[ix] ^= header_table[ix];
192 }
193
KFileIsWGAEnc(const void * buffer,size_t buffer_size)194 KRYPTO_EXTERN rc_t CC KFileIsWGAEnc (const void * buffer, size_t buffer_size)
195 {
196 const uint8_t * ph;
197 const uint8_t * pt;
198 const uint8_t * pb;
199 size_t ix;
200 size_t lim;
201
202 if ((buffer == NULL) || (buffer_size == 0))
203 return RC (rcFS, rcFile, rcIdentifying, rcParam, rcNull);
204
205 /* bare minimum size to identify we decide is the first 8
206 * obsfucated ASCII bytes */
207
208 if (buffer_size < (sizeof (header_const.magic) - 1))
209 return RC (rcFS, rcFile, rcIdentifying, rcBuffer, rcInsufficient);
210
211 /* a match is ph[X] ^ pt[X] == pb[X] at specially identified points
212 * we could have put the ^ into a constant for a tad faster operation
213 * but meh...
214 */
215 ph = (const uint8_t*)&header_const;
216 pt = (const uint8_t*)&header_table;
217 pb = (const uint8_t*)buffer;
218
219 ix = offsetof (struct KWGAEncFileHeader, magic);
220 lim = ix + sizeof (header_const.magic);
221 if (lim > buffer_size)
222 lim = buffer_size;
223 for (; (ix < lim) && (ix < buffer_size); ++ix)
224 if ((ph[ix] ^ pt[ix]) != pb[ix])
225 return SILENT_RC (rcFS, rcFile, rcIdentifying, rcFile, rcWrongType);
226
227 if (buffer_size < offsetof (struct KWGAEncFileHeader, fer_enc))
228 return 0;
229
230 if ((ph[offsetof (struct KWGAEncFileHeader, fer_enc)] ^ pt[offsetof (struct KWGAEncFileHeader, fer_enc)])
231 != pb[offsetof (struct KWGAEncFileHeader, fer_enc)])
232 return RC (rcFS, rcFile, rcIdentifying, rcFile, rcWrongType);
233
234
235 ix = offsetof (struct KWGAEncFileHeader, reserved);
236 if (buffer_size < ix)
237 return 0;
238
239 lim = ix + sizeof (header_const.reserved);
240
241 if (lim > buffer_size)
242 lim = buffer_size;
243
244 for (; ix < lim; ++ix)
245 if ((ph[ix] ^ pt[ix]) != pb[ix])
246 return RC (rcFS, rcFile, rcIdentifying, rcFile, rcWrongType);
247
248 return 0;
249 }
250
251
252
253
254
255
256 /* ======================================================================
257 * KWGAEncFile
258 */
259
260 /* -----
261 * define the specific types to be used in the templatish/inheritancish
262 * definition of vtables and their elements
263 */
264 typedef struct KWGAEncFile KWGAEncFile;
265 #define KFILE_IMPL struct KWGAEncFile
266
267 #include <kfs/impl.h>
268
269 struct KWGAEncFile
270 {
271 KFile dad; /* base class */
272 const KFile * encrypted; /* encrypted file as a read only KFile */
273 uint64_t file_size; /* as read from the encrypted file header */
274 KTime_t mtime; /* as read from the encrypted file header */
275 uint32_t block_size; /* block size is in KB not bytes */
276 KCipher * cipher;
277
278 char inkey [32];
279 size_t inkey_size;
280
281 bool md5_here; /* was the md5 in the header? */
282 char md5[32]; /* external md5 loaded into the header as ASCII hex */
283 char md51[32]; /* md5 of first block loaded into the header as ASCII hex */
284 struct
285 {
286 uint64_t offset; /* position with in the unencrypted file */
287 uint32_t valid; /* how much usable at data */
288 uint8_t data [DEFAULT_BUFF_SIZE];
289 } buffer;
290 };
291
292
293 /* ----------------------------------------------------------------------
294 * InitKey
295 *
296 * This method mimics the prepare_key method in the WGA C++ code including
297 * what looks like programming errors.
298 *
299 * The goal is compatibility not 'correctness'.
300 */
301 static
KWGAEncFileKeyInit(KWGAEncFile * self,const char * key,size_t key_size)302 rc_t KWGAEncFileKeyInit (KWGAEncFile * self, const char * key, size_t key_size)
303 {
304 rc_t rc;
305 char g_key [32]; /* original used 513 but no key uses that much */
306
307 /* scary! */
308 /* this is copied directly from enc_read.cpp with all the 'possibly wrong' code */
309 memset (g_key, 0, sizeof g_key);
310 if (key_size > sizeof (g_key))
311 key_size = sizeof (g_key);
312 memmove (g_key, key, key_size);
313
314 if (key_size < 16) /* even though g_key and some cipher keys are larger than 16 */
315 {
316 size_t jx, ix;
317 for ((jx = key_size),(ix = 0); jx < 16; ++jx, ++ix)
318 {
319 g_key[jx] = self->md51[ix] | g_key[ix%(jx?jx:1)]; /* cringe? */
320 }
321 }
322
323 /* okay we'll use 32 bytes from g_key though is is highly
324 * likely we've only got 16 non-zero bytes */
325 rc = KCipherSetDecryptKey (self->cipher, g_key, WGA_AES_BITS/8);
326
327 return rc;
328 }
329
330
331 /* ----------------------------------------------------------------------
332 * Destroy
333 *
334 */
335 static
KWGAEncFileDestroyRead(KWGAEncFile * self)336 rc_t CC KWGAEncFileDestroyRead (KWGAEncFile *self)
337 {
338 rc_t rc = 0;
339
340 if (self)
341 {
342 rc = KFileRelease (self->encrypted);
343 free (self);
344 }
345 return rc;
346 }
347
348
349 /* ----------------------------------------------------------------------
350 * GetSysFile
351 * returns an underlying system file object
352 * and starting offset to contiguous region
353 * suitable for memory mapping, or NULL if
354 * no such file is available.
355 *
356 * We do not allow this for read, write or update as you can not memory map the
357 * unencrypted file in a meaningful way.
358 */
359 static
KWGAEncFileGetSysFile(const KWGAEncFile * self,uint64_t * offset)360 struct KSysFile *CC KWGAEncFileGetSysFile (const KWGAEncFile *self, uint64_t *offset)
361 {
362 assert (self);
363 assert (offset);
364 return NULL;
365 }
366
367
368 /* ----------------------------------------------------------------------
369 * RandomAccess
370 *
371 * returns 0 if random access, error code otherwise
372 */
373 static
KWGAEncFileRandomAccess(const KWGAEncFile * self)374 rc_t CC KWGAEncFileRandomAccess (const KWGAEncFile *self)
375 {
376 assert (self != NULL);
377 assert (self->encrypted != NULL);
378 return KFileRandomAccess (self->encrypted);
379 }
380
381
382 /* ----------------------------------------------------------------------
383 * Size
384 * returns size in bytes of file
385 *
386 * "size" [ OUT ] - return parameter for file size
387 */
388 static
KWGAEncFileSize(const KWGAEncFile * self,uint64_t * size)389 rc_t CC KWGAEncFileSize (const KWGAEncFile *self, uint64_t *size)
390 {
391 uint64_t esize;
392 rc_t rc;
393 assert (self != NULL);
394 assert (self->encrypted != NULL);
395 assert (size != NULL);
396
397 *size = 0;
398 rc = KFileSize (self->encrypted, &esize);
399 if (rc == 0)
400 *size = esize - sizeof (KWGAEncFileHeader);
401 return rc;
402 }
403
404
405 /* ----------------------------------------------------------------------
406 * SetSize
407 * sets size in bytes of file
408 *
409 * "size" [ IN ] - new file size
410 */
411 static
KWGAEncFileSetSize(KWGAEncFile * self,uint64_t size)412 rc_t CC KWGAEncFileSetSize (KWGAEncFile *self, uint64_t size)
413 {
414 assert (self);
415
416 return RC ( rcFS, rcFile, rcUpdating, rcFunction, rcUnsupported );
417 }
418
419
420 /* ----------------------------------------------------------------------
421 * Read
422 * read file from known position
423 *
424 * "pos" [ IN ] - starting position within file
425 *
426 * "buffer" [ OUT ] and "bsize" [ IN ] - return buffer for read
427 *
428 * "num_read" [ OUT, NULL OKAY ] - optional return parameter
429 * giving number of bytes actually read
430 */
431
432 /* local fill the buffer with block or more */
433 static
KWGAEncFileReadInt(KWGAEncFile * self,uint64_t pos,size_t bsize)434 rc_t KWGAEncFileReadInt (KWGAEncFile * self, uint64_t pos, size_t bsize)
435 {
436 uint64_t adjpos;
437 size_t tot_read;
438 size_t num_read;
439 rc_t rc = 0;
440
441 assert (self);
442 assert (bsize);
443 assert (bsize <= 32 * 1024);
444 assert (128%16 == 0);
445
446 memset (self->buffer.data, 0, sizeof self->buffer.data);
447 tot_read = num_read = 0;
448 adjpos = pos + sizeof (KWGAEncFileHeader);
449 #if 0
450 do
451 {
452 rc = KFileRead (self->encrypted, adjpos + tot_read,
453 self->buffer.data + tot_read, bsize - tot_read,
454 &num_read);
455 if (rc)
456 return rc;
457 tot_read += num_read;
458 } while ((tot_read < bsize) && (num_read > 0));
459 #else
460 rc = KFileReadAll (self->encrypted, adjpos, self->buffer.data, bsize,
461 &tot_read);
462 #endif
463 self->buffer.offset = pos;
464 self->buffer.valid = (uint32_t)tot_read;
465
466 if (tot_read & 15)
467 rc = RC (rcKrypto, rcFile, rcReading, rcSize, rcInsufficient);
468 else if (tot_read > 0)
469 {
470 #if 1
471
472 #if RETAINED_COMPATIBILTY_WITH_ERROR
473 KCipherDecryptECB (self->cipher, self->buffer.data, self->buffer.data,
474 (uint32_t)(tot_read / ECB_BYTES));
475 #else
476 /* Well this is wrong for even being wrong now */
477 KCipherDecryptECB (self->cipher, self->buffer.data, self->buffer.data,
478 tot_read);
479 #endif
480
481 #else
482 uint32_t count;
483 uint32_t ix;
484
485 /* this loop can be replaced by the KCipherDecryptECB
486 * with care taken to match the error in the original
487 * C++
488 */
489
490 #if RETAINED_COMPATIBILTY_WITH_ERROR
491 count = tot_read / ECB_BYTES;
492 #else
493 /* do all full 16 byte blocks plus a last partial block */
494 count = (tot_read + (ECB_BYTES-1)) / ECB_BYTES;
495 #endif
496 for (ix = 0; ix < count; ++ix)
497 {
498 uint8_t * pb = self->buffer.data + (ix * ECB_BYTES);
499
500 rc = KCipherDecrypt (self->cipher, pb, pb);
501 if (rc)
502 break;
503 }
504 #endif
505 }
506 return rc;
507 }
508
509
510 static
KWGAEncFileRead(const KWGAEncFile * cself,uint64_t pos,void * buffer,size_t bsize,size_t * num_read)511 rc_t CC KWGAEncFileRead (const KWGAEncFile *cself,
512 uint64_t pos,
513 void *buffer,
514 size_t bsize,
515 size_t *num_read)
516 {
517 KWGAEncFile * self = (KWGAEncFile *)cself; /* mutable values */
518 rc_t rc = 0;
519
520 assert (cself);
521 assert (buffer);
522 assert (num_read);
523
524
525 *num_read = 0;
526
527 /* are we even within the file? If not just say no. Drugs are bad Mmmkay */
528 if (pos >= self->file_size)
529 {}
530 /* are we not reading from out what is already in the decrypted buffer space? */
531 else
532 {
533 if ((self->buffer.valid == 0) ||
534 (pos < self->buffer.offset) ||
535 (pos >= (self->buffer.offset + self->buffer.valid)))
536 {
537 if (pos < self->block_size) /* we'll be reading from the first 'block' */
538 {
539 rc = KWGAEncFileReadInt (self, 0, self->block_size);
540 if (rc)
541 {
542 LOGERR (klogErr, rc, "error reading first data block of"
543 " encrypted file");
544 return rc;
545 }
546 if (self->buffer.valid > self->block_size)
547 {
548 rc = RC (rcFS, rcFile, rcReading, rcBuffer, rcTooBig);
549 LOGERR (klogInt, rc, "read wrong amount for first block");
550 return rc;
551 }
552 else
553 {
554 size_t md5_size;
555 size_t nudge = 0;
556 char md51_comp [32];
557
558 if (self->buffer.valid & (ECB_BYTES-1))
559 nudge = ECB_BYTES - (self->buffer.valid & (ECB_BYTES-1));
560 if (nudge)
561 memset (self->buffer.data + self->buffer.valid, 0, nudge);
562
563 md5_size = self->buffer.valid + nudge;
564
565 CalcMD5 (self->buffer.data, md5_size, md51_comp);
566
567 #if 1
568 if (strcase_cmp (self->md51, string_size(self->md51),
569 md51_comp, string_size(md51_comp), 32) != 0)
570 #else
571 if (strncasecmp (self->md51, md51_comp, 32) != 0)
572 #endif
573 {
574 rc = RC (rcFS, rcFile, rcReading, rcConstraint, rcCorrupt);
575 LOGERR (klogErr, rc, "MD5 does not match in decryption");
576 return rc;
577 }
578 }
579 }
580 else /* if (pos >= self->block_size) */
581 {
582 rc = KWGAEncFileReadInt (self, (pos & ~ ( uint64_t ) (16-1)),
583 DEFAULT_BUFF_SIZE);
584 if (rc)
585 {
586 LOGERR (klogErr, rc, "error reading data block of"
587 " encrypted file");
588 return rc;
589 }
590 } /* if (pos < self->block_size) */
591 } /* if ((self->buffer.valid == 0) || etc. */
592 /* if here we have a valid buffer and it contains the start pos requested */
593 /* assert (pos >= self->buffer.offset); */
594 /* assert (pos < (self->buffer.offset +self->buffer.valid)); */
595 {
596 size_t start;
597 size_t limit;
598
599 /* find offset of start for copy within the buffer */
600 start = (size_t)(pos - self->buffer.offset);
601 /* how many bytes available starting here */
602 limit = self->buffer.valid - start;
603
604 if (pos + limit > self->file_size)
605 limit = self->file_size - pos;
606
607 /* are we asking to read more than we have? is so trim the request */
608 if (limit < bsize)
609 bsize = limit;
610
611 memmove (buffer, self->buffer.data + start, bsize);
612 *num_read = bsize;
613 }
614 }
615 return 0;
616 }
617
618
619 /* ----------------------------------------------------------------------
620 * Write
621 * write file at known position
622 *
623 * "pos" [ IN ] - starting position within file
624 *
625 * "buffer" [ IN ] and "size" [ IN ] - data to be written
626 *
627 * "num_writ" [ OUT, NULL OKAY ] - optional return parameter
628 * giving number of bytes actually written
629 *
630 * Unsupported as we now treat archives as READ ONLY
631 */
632 static
KWGAEncFileWriteFail(KWGAEncFile * self,uint64_t pos,const void * buffer,size_t bsize,size_t * num_writ)633 rc_t CC KWGAEncFileWriteFail (KWGAEncFile *self, uint64_t pos,
634 const void *buffer, size_t bsize,
635 size_t *num_writ)
636 {
637 assert (self);
638 return RC (rcFS, rcFile, rcReading, rcFunction, rcUnsupported);
639 }
640
641
642 /* ----------------------------------------------------------------------
643 * Type
644 * returns a KFileDesc
645 * not intended to be a content type,
646 * but rather an implementation class
647 */
648 static
KWGAEncFileType(const KWGAEncFile * self)649 uint32_t CC KWGAEncFileType (const KWGAEncFile *self)
650 {
651 assert (self != NULL);
652 assert (self->encrypted != NULL);
653
654 return KFileType (self->encrypted);
655 }
656
657
658 static const KFile_vt_v1 vtKWGAEncFileRead =
659 {
660 /* version */
661 1, 1,
662
663 /* 1.0 */
664 KWGAEncFileDestroyRead,
665 KWGAEncFileGetSysFile,
666 KWGAEncFileRandomAccess,
667 KWGAEncFileSize,
668 KWGAEncFileSetSize,
669 KWGAEncFileRead,
670 KWGAEncFileWriteFail,
671
672 /* 1.1 */
673 KWGAEncFileType
674 };
675
676
677 static
KWGAEncFileHeaderRead(KWGAEncFile * self)678 rc_t KWGAEncFileHeaderRead (KWGAEncFile * self)
679 {
680 KWGAEncFileHeader header;
681 uint8_t * pb;
682 size_t num_read;
683 size_t tot_read;
684 rc_t rc;
685
686 assert (self);
687 assert (sizeof (KWGAEncFileHeader) == 128);
688
689
690 DEBUG_STS (("s: Enter '%p'\n", __func__, self));
691 pb = (void*)&header;
692 for (num_read = tot_read = 0; tot_read < sizeof header; )
693 {
694 rc = KFileRead (self->encrypted, (uint64_t)tot_read, pb,
695 sizeof (header) - tot_read, &num_read);
696 if (rc)
697 {
698 LOGERR (klogErr, rc, "Error reading the header for an encrypted file");
699 return rc;
700 }
701
702 if (num_read == 0)
703 {
704 rc = RC (rcFS, rcFile, rcReading, rcFile, rcInsufficient);
705 LOGERR (klogErr, rc, "Header incomplete for an encrypted file");
706 return rc;
707 }
708 tot_read += num_read;
709 pb += num_read;
710 }
711
712 KWGAEncFileHeaderDecrypt (&header);
713
714 if (memcmp (header.magic, ncbi_crypt_magic, sizeof ncbi_crypt_magic) != 0)
715 {
716 rc = RC (rcFS, rcFile, rcReading, rcHeader, rcCorrupt);
717 LOGERR (klogErr, rc, "Header's magic bad for encrypted file");
718 return rc;
719 }
720
721 /* so far unknown legal range */
722 self->block_size = strtou32 (header.block_sz, NULL, KWGA_ENC_FILE_HEADER_RADIX);
723
724 self->file_size = strtou64 (header.file_sz, NULL, KWGA_ENC_FILE_HEADER_RADIX);
725
726 /* file format has limiting feature of a 32 bit timestamp */
727 self->mtime = (KTime_t)strtol (header.mtime, NULL, KWGA_ENC_FILE_HEADER_RADIX);
728
729 switch ((FER_ENCODING)header.fer_enc)
730 {
731 default:
732 rc = RC (rcFS, rcFile, rcReading, rcHeader, rcOutofrange);
733 LOGERR (klogErr, rc, "Enryption type code out of range");
734 return rc;
735 case fer_encDES:
736 case fer_encBLF:
737 rc = RC (rcFS, rcFile, rcReading, rcHeader, rcIncorrect);
738 LOGERR (klogErr, rc, "Enryption type code not supported");
739 return rc;
740 case fer_encAES:
741 break;
742 }
743
744 self->md5_here = (header.md5_here != 0);
745
746 if (self->md5_here)
747 memmove (self->md5, header.md5, sizeof (self->md5));
748
749 memmove (self->md51, header.md51, sizeof (self->md51));
750
751 return 0; /* yeah not really checking later errors am i? */
752 }
753
754 /* ----------------------------------------------------------------------
755 * KWGAEncFileMake
756 * create a new file object
757 *
758 * pself where to put pointer to new object
759 * encrypted a readable file that is encrypted
760 * cipher a cipher object that must support AES with 256 bit key
761 * not verified except through failure to decrypt a file
762 * key bytes containing the user key - should be utf-8 but with no
763 * control characters
764 * key_size how many bytes to use from the key
765 */
766 /* read only version for decrypting of existing files */
KFileMakeWGAEncRead(const struct KFile ** pself,const struct KFile * encrypted,const char * key,size_t key_size)767 KRYPTO_EXTERN rc_t CC KFileMakeWGAEncRead (const struct KFile ** pself,
768 const struct KFile * encrypted,
769 const char * key,
770 size_t key_size)
771 {
772 rc_t rc;
773
774 if (pself == NULL)
775 {
776 rc = RC (rcFS, rcFile, rcConstructing, rcSelf, rcNull);
777 LOGERR (klogErr, rc, "key parameter for WGA encrypted file is empty");
778 return rc;
779 }
780
781 *pself = NULL;
782
783 if ((encrypted == NULL)||(key == NULL))
784 {
785 rc = RC (rcFS, rcFile, rcConstructing, rcParam, rcNull);
786 LOGERR (klogErr, rc, "missing WGA encrypted file passed in to constructor");
787 }
788
789 else if (key_size == 0)
790 {
791 rc = RC (rcFS, rcFile, rcConstructing, rcParam, rcInvalid);
792 LOGERR (klogErr, rc, "missing WGA encrypted file passed in to constructor");
793 }
794
795 else if (encrypted->read_enabled == 0)
796 {
797 rc = RC (rcFS, rcFile, rcConstructing, rcParam, rcIncorrect);
798 LOGERR (klogErr, rc, "encrypted file not readable");
799 }
800 else
801 {
802 KCipherManager * cipher_mgr;
803
804 rc = KCipherManagerMake (&cipher_mgr);
805 if (rc == 0)
806 {
807 KCipher * cipher;
808
809 rc = KCipherManagerMakeCipher (cipher_mgr, &cipher, kcipher_AES);
810 if (rc == 0)
811 {
812 size_t z;
813
814 rc = KCipherBlockSize (cipher, &z);
815 if (rc)
816 {
817 LOGERR (klogErr, rc, "unable to get block size for WGA "
818 "encrypted file cipher passed in to constructor");
819 }
820 else
821 {
822 if (z != ECB_BYTES)
823 {
824 rc = RC (rcFS, rcFile, rcConstructing, rcParam,
825 rcInvalid);
826 LOGERR (klogErr, rc, "wrong block size for WGA "
827 "encrypted file cipher passed in to "
828 "constructor");
829 }
830 else
831 {
832 KWGAEncFile * self;
833
834 self = calloc (sizeof (*self), sizeof (uint8_t));
835 if (self == NULL)
836 {
837 rc = RC (rcFS, rcFile, rcConstructing, rcMemory,
838 rcExhausted);
839 LOGERR (klogErr, rc, "out of memory while "
840 "constructing decryptor");
841 }
842 else
843 {
844 rc = KFileAddRef (encrypted);
845 if (rc)
846 LOGERR (klogErr, rc, "unable to add reference "
847 "to encrypted file");
848 else
849 {
850 /* cast to strip const */
851 self->encrypted = encrypted;
852 self->cipher = cipher;
853
854 /* read the header of the encrypted file for
855 * details about the decrypted file */
856 DEBUG_STS(("%s: calling KWGAEncFileHeaderRead\n",
857 __func__));
858 rc = KWGAEncFileHeaderRead (self);
859 if (rc == 0)
860 {
861 /* using the file header's stored encoding
862 * key build a key from the parameter key */
863 DEBUG_STS(("%s: calling "
864 "KWGAEncFileHeaderRead\n",
865 __func__));
866 rc = KWGAEncFileKeyInit (self, key,
867 key_size);
868 }
869 if (rc == 0)
870 {
871 rc = KFileInit (&self->dad,
872 (const KFile_vt*)
873 &vtKWGAEncFileRead,
874 "KWGAEncFile", "no-name",
875 true, false);
876 if (rc)
877 LOGERR (klogInt, rc, "Failed to initialize decrypting file");
878 else
879 {
880 *pself = &self->dad;
881 self->buffer.offset = 0;
882 self->buffer.valid = 0;
883 KCipherManagerRelease (cipher_mgr);
884 return 0;
885 }
886 }
887 /* release of encrypted handled in destroy() */
888 }
889 KWGAEncFileDestroyRead (self);
890 }
891 }
892 }
893 KCipherRelease (cipher);
894 }
895 }
896 }
897 return rc;
898 }
899
900 static __inline__
WGAEncValidateHeader(const KWGAEncFileHeader * header,size_t header_size)901 rc_t WGAEncValidateHeader (const KWGAEncFileHeader * header,
902 size_t header_size)
903 {
904 rc_t rc = 0;
905 unsigned ix;
906
907 if (header_size != sizeof * header)
908 {
909 rc = RC (rcKrypto, rcFile, rcValidating, rcHeader, rcInsufficient);
910 LOGERR (klogErr, rc, "incomplete header");
911 return rc;
912 }
913
914 if (memcmp (header, &header_const, sizeof header->magic) != 0)
915 {
916 rc = RC (rcKrypto, rcFile, rcValidating, rcHeader, rcInvalid);
917 LOGERR (klogErr, rc, "bad signature in encrypted file header");
918 return rc;
919 }
920
921 #if 0
922 /* check block_size */
923 do
924 {
925 for (ix = 0; isspace(header->block_sz[ix]); ++ix)
926 ;
927
928 if (ix >= sizeof (header->block_sz))
929 break;
930
931 if (header->block_sz[ix] == '+')
932 ++ix;
933
934 if (ix >= sizeof (header->block_sz))
935 break;
936
937 for (; (ix < sizeof header->block_sz); ++ix)
938 {
939 if (isdigit (header->block_sz[ix]))
940 ;
941 else if ((header->block_sz[ix] >= 'a') &&
942 (header->block_sz[ix] <= 'w'))
943 ;
944 else if ((header->block_sz[ix] >= 'A') &&
945 (header->block_sz[ix] <= 'W'))
946 ;
947 else
948 break;
949 }
950 for (; (ix < sizeof header->block_sz); ++ix)
951 {
952 if (header->block_sz[ix] == '\0')
953 ;
954 else
955 break;
956 }
957 if (rc)
958
959
960
961 for (ix = 0; ix < sizeof header->block_size; ++ix)
962 {
963 if ()
964 ;
965 }
966
967 ;
968 else if (header->block_sz[ix] == '-')
969 ;
970 else
971 ;
972 }while (0);
973 /* check file_sz */
974 /* check mtime */
975 #endif
976
977 if (header->fer_enc != fer_encAES)
978 {
979 rc = RC (rcKrypto, rcFile, rcValidating, rcHeader, rcInvalid);
980 LOGERR (klogErr, rc, "bad encoding flag in encrypted file header");
981 return rc;
982 }
983
984 if ((header->md5_here != true) && (header->md5_here != false))
985 {
986 rc = RC (rcKrypto, rcFile, rcValidating, rcHeader, rcInvalid);
987 LOGERR (klogErr, rc, "bad checksum flag in encrypted file header");
988 return rc;
989 }
990
991 /* check md5 */
992 /* check md51 */
993
994 for (ix = 0; ix < sizeof header->reserved; ++ix)
995 {
996 if (header->reserved[ix] != 0)
997 {
998 rc = RC (rcKrypto, rcFile, rcValidating, rcHeader, rcInvalid);
999 LOGERR (klogErr, rc, "bad reserved space in encrypted file header");
1000 return rc;
1001 }
1002 }
1003
1004 return 0;
1005 }
1006
1007
WGAEncValidate(const KFile * encrypted,const char * key,size_t key_size)1008 KRYPTO_EXTERN rc_t CC WGAEncValidate (const KFile * encrypted,
1009 const char * key,
1010 size_t key_size)
1011 {
1012 const KFile * buffile;
1013 rc_t rc, orc;
1014
1015 rc = KBufFileMakeRead (&buffile, encrypted, 64*1024);
1016 if (rc)
1017 LOGERR (klogErr, rc, "unable to buffer encrypted file");
1018
1019 else
1020 {
1021 KWGAEncFileHeader header;
1022 size_t num_read;
1023
1024 /* first let's disect the header */
1025 rc = KFileReadAll (buffile, 0, &header, sizeof header, &num_read);
1026 if (rc)
1027 LOGERR (klogErr, rc, "unable to read encrypted file header");
1028
1029 else
1030 {
1031 uint64_t file_size;
1032 uint64_t header_file_size;
1033 char ascii_md5 [32];
1034 char header_ascii_md5 [32];
1035
1036 KWGAEncFileHeaderDecrypt (&header);
1037
1038 rc = WGAEncValidateHeader (&header, num_read);
1039 if (rc)
1040 ;
1041
1042 else
1043 {
1044 if (key_size == 0)
1045 {
1046 const KFile * countfile;
1047 rc = KFileMakeCounterRead (&countfile, encrypted, &file_size, NULL, false);
1048 if (rc)
1049 {
1050 LOGERR (klogErr, rc, "error making file size counter");
1051 file_size = 0;
1052 }
1053 else
1054 {
1055 KFileAddRef (encrypted);
1056 KFileRelease (countfile);
1057
1058 file_size -= sizeof header;
1059 }
1060 }
1061 else
1062 {
1063 union
1064 {
1065 const KFile * decrypted;
1066 const KWGAEncFile * decryptor;
1067 } u;
1068
1069 rc = KFileMakeWGAEncRead (&u.decrypted, buffile, key, key_size);
1070 if (rc)
1071 LOGERR (klogErr, rc, "error making decryptor");
1072
1073 else
1074 {
1075 KFile * nullfile;
1076
1077 rc = KFileMakeNullUpdate (&nullfile);
1078 if (rc)
1079 LOGERR (klogInt, rc, "error making data sync");
1080
1081 else
1082 {
1083 KMD5SumFmt * fmt;
1084 static const char name[] = "wgaencrypt";
1085
1086 rc = KMD5SumFmtMakeUpdate (&fmt, nullfile);
1087 if (rc)
1088 {
1089 LOGERR (klogInt, rc, "error making md5sum database");
1090 KFileRelease (nullfile);
1091 }
1092 else
1093 {
1094 const KFile * md5file;
1095
1096 rc = KFileMakeNewMD5Read (&md5file, u.decrypted, fmt, name);
1097 if (rc)
1098 LOGERR (klogInt, rc, "error making MD5 calculator");
1099
1100 else
1101 {
1102 /*
1103 * ill mannered md5file steals
1104 * references instead of adding one
1105 */
1106 rc = KFileAddRef (u.decrypted);
1107 if (rc)
1108 LOGERR (klogInt, rc, "error adding reference to decryptor");
1109
1110 else
1111 {
1112
1113 const KFile * countfile;
1114 rc = KFileMakeCounterRead (&countfile,
1115 md5file,
1116 &file_size, NULL,
1117 true);
1118 if (rc)
1119 LOGERR (klogErr, rc,
1120 "error making file size "
1121 "counter");
1122 else
1123 {
1124 /*
1125 * ill mannered countfile steals
1126 * references instead of adding one
1127 */
1128 KFileAddRef (md5file);
1129 rc = KFileRelease (countfile);
1130 }
1131
1132 orc = KFileRelease (md5file);
1133
1134 if (rc == 0)
1135 rc = orc;
1136
1137 if (rc == 0)
1138 {
1139 uint8_t md5 [16];
1140 bool bin;
1141
1142 rc = KMD5SumFmtFind (fmt, name, md5, &bin);
1143 if (rc)
1144 LOGERR (klogInt, rc, "error locating MD5");
1145
1146 else
1147 {
1148 size_t zz;
1149 unsigned ix;
1150 for (ix = 0; ix < 16; ++ix)
1151 {
1152 string_printf (&ascii_md5[2*ix], 2, &zz, "%2.2x",
1153 (unsigned)(uint8_t)md5[ix]);
1154 }
1155 memmove (header_ascii_md5,
1156 u.decryptor->md5,
1157 sizeof header_ascii_md5);
1158 }
1159 }
1160 }
1161 }
1162 KMD5SumFmtRelease (fmt);
1163 }
1164 }
1165 KFileRelease (u.decrypted);
1166 }
1167 }
1168 }
1169 if (rc == 0)
1170 {
1171 uint64_t sys_file_size;
1172 uint64_t pad_file_size = 0;
1173 rc_t orc;
1174
1175 /* ccheck file size */
1176
1177 orc = KFileSize (encrypted, &sys_file_size);
1178 if (orc == 0)
1179 {
1180 pad_file_size = file_size + 15;
1181 pad_file_size &= ~ ( uint64_t ) 15;
1182 pad_file_size += sizeof (KWGAEncFileHeader);
1183 }
1184 header_file_size = strtou64 (header.file_sz, NULL, KWGA_ENC_FILE_HEADER_RADIX);
1185
1186 if (key_size == 0)
1187 {
1188 header_file_size += 15;
1189 header_file_size &= ~ ( uint64_t ) 15;
1190 }
1191
1192 if (file_size < header_file_size)
1193 rc = RC (rcKrypto, rcFile, rcValidating, rcSize, rcInsufficient);
1194
1195 else if (file_size > header_file_size)
1196 rc = RC (rcKrypto, rcFile, rcValidating, rcSize, rcExcessive);
1197
1198 else if ((orc == 0) &&
1199 (sys_file_size > pad_file_size))
1200 rc = RC (rcKrypto, rcFile, rcValidating, rcSize, rcExcessive);
1201
1202 /* check md5 */
1203 else if (!header.md5_here)
1204 {
1205 /* rc = RC (rcKrypto, rcFile, rcValidating, rcEncryption, rcNotFound); */
1206 }
1207 else if (key_size == 0)
1208 rc = RC (rcKrypto, rcFile, rcValidating, rcEncryption, rcNull);
1209
1210 else
1211 {
1212 int cmp;
1213
1214 cmp = strcase_cmp (ascii_md5, sizeof ascii_md5,
1215 header_ascii_md5, sizeof header_ascii_md5,
1216 sizeof ascii_md5);
1217 if (cmp)
1218 {
1219 rc = RC (rcKrypto, rcFile, rcValidating, rcChecksum,
1220 rcInvalid);
1221 LOGERR (klogErr, rc, "Encrypted file MD5 does not match");
1222 }
1223 }
1224 }
1225 }
1226 KFileRelease (buffile);
1227 }
1228 return rc;
1229 }
1230 #if 0
1231 /* not working as planned */
1232 if (rc == 0)
1233 {
1234 uint32_t this_read;
1235
1236 /* try to read past size now obtained */
1237 do
1238 {
1239 uint8_t throwaway [8192];
1240
1241 rc = KFileRead (encrypted, z + sizeof (KWGAEncFileHeader), throwaway, sizeof throwaway,
1242 &this_read);
1243 if (rc)
1244 {
1245 LOGERR (klogErr, rc, "Error reading file");
1246 break;
1247 }
1248 z += this_read;
1249
1250 } while (this_read);
1251 }
1252 #endif
1253
1254 /* end of file wgaencrypt.c */
1255
1256