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