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 #include <krypto/extern.h>
26 #include <krypto/reencfile.h>
27 #include <krypto/key.h>
28 #include <krypto/encfile.h>
29 #include <krypto/encfile-priv.h>
30 #include "encfile-priv.h"
31 
32 #include <klib/rc.h>
33 #include <klib/checksum.h>
34 #include <klib/log.h>
35 #include <klib/out.h>
36 #include <klib/debug.h>
37 #include <klib/vector.h>
38 #include <klib/status.h>
39 #include <kfs/file.h>
40 #include <kfs/ramfile.h>
41 
42 #include <sysalloc.h>
43 
44 #include <byteswap.h>
45 
46 #include <stdlib.h>
47 #include <string.h>
48 #include <assert.h>
49 
50 #include <klib/out.h>
51 
52 #define USE_MISSING_VECTOR false
53 
54 /* ----------------------------------------------------------------------
55  * KReencFile
56  *   Base object class for the encryption, decryption and validation of
57  *   the file format defined above
58  */
59 #define KFILE_IMPL KReencFile
60 #include <kfs/impl.h>
61 
62 /* -----
63  */
64 struct KReencFile
65 {
66     KFile         dad;          /* base class */
67     const KFile * encrypted;    /* encrypted file we start from */
68     const KFile * dec;          /* decryptor of the original */
69     KFile *       enc;          /* encryptor */
70     KFile *       ram;          /* a file that works from a sliding window buffer */
71 
72     size_t        num_read;
73     size_t        num_writ;
74 /* block id's can not max out a 64 bit number as that is a file that is 32K times too big */
75 #define NO_CURRENT_BLOCK (~(uint64_t)0)
76     uint64_t      block_id;
77     uint64_t      footer_block; /* if zero, file does not have any content blocks */
78 
79     uint64_t      size;         /* size as known from the original file [see known_size] */
80     bool          known_size;   /* is the size of the original file known? */
81 
82     char          plain_text  [ENC_DATA_BLOCK_SIZE];
83     union
84     {
85         KEncFileBlock block;
86         char          text [sizeof (KEncFileBlock)];
87     } block;
88     union
89     {
90         KEncFileFooter foot;
91         char           text [sizeof (KEncFileFooter)];
92     } foot;
93 
94 };
95 
96 /* ----------------------------------------------------------------------
97  * Interface Functions
98  *
99  * Destroy
100  *
101  */
102 static
KReencFileDestroy(KReencFile * self)103 rc_t CC KReencFileDestroy (KReencFile *self)
104 {
105     if (self)
106     {
107         rc_t rc1, rc2, rc3, rc4;
108 
109         rc1 = KFileRelease (self->encrypted);
110         if (rc1)
111             LOGERR (klogInt, rc1, "Re-enc failed to release encrypted file");
112 
113         rc2 = KFileRelease (self->dec);
114         if (rc2)
115             LOGERR (klogInt, rc2, "Re-enc failed to release decryptor");
116 
117         rc3 = KFileRelease (self->ram);
118         if (rc3)
119             LOGERR (klogInt, rc3, "Re-enc failed to release ram file");
120 
121         rc4 = KFileRelease (self->enc);
122         if (rc4)
123             LOGERR (klogInt, rc4, "Re-enc failed to release encryptor");
124 
125         free (self);
126 
127         return (rc1 ? rc1 :
128                 rc2 ? rc2 :
129                 rc3 ? rc3 :
130                 rc4);
131     }
132     return 0;
133 }
134 
135 
136 /* ----------------------------------------------------------------------
137  * GetSysFile
138  *  returns an underlying system file object
139  *  and starting offset to contiguous region
140  *  suitable for memory mapping, or NULL if
141  *  no such file is available.
142  *
143  * We do not allow this for read, write or update as you can not memory map the
144  * unencrypted file in a meaningful way.
145  */
146 static
KReencFileGetSysFileUnsupported(const KReencFile * self,uint64_t * offset)147 struct KSysFile *CC KReencFileGetSysFileUnsupported (const KReencFile *self, uint64_t *offset)
148 {
149     assert (self);
150     assert (offset);
151 
152     return NULL;
153 }
154 
155 
156 /* ----------------------------------------------------------------------
157  * RandomAccess
158  *
159  *  returns 0 if random access, error code otherwise
160  */
161 static
KReencFileRandomAccess(const KReencFile * self)162 rc_t CC KReencFileRandomAccess (const KReencFile *self)
163 {
164     assert (self != NULL);
165     assert (self->encrypted != NULL);
166 
167     return KFileRandomAccess (self->encrypted);
168 }
169 
170 
171 /* ----------------------------------------------------------------------
172  * Size
173  *  returns size in bytes of file
174  *
175  *  "size" [ OUT ] - return parameter for file size
176  */
177 static
KReencFileSize(const KReencFile * self,uint64_t * size)178 rc_t CC KReencFileSize (const KReencFile *self, uint64_t *size)
179 {
180     assert (self != NULL);
181     assert (self->encrypted != NULL);
182 
183     /* -----
184      * the re-encrypted file will be the same size as the
185      * previously encrypted file and we have the same understanding
186      * about knowing the size
187      */
188     return KFileSize (self->encrypted, size);
189 }
190 
191 
192 static
KEncryptFileSize(const KReencFile * self,uint64_t * size)193 rc_t CC KEncryptFileSize (const KReencFile *self, uint64_t *size)
194 {
195     uint64_t z;
196     rc_t rc;
197 
198     assert (self != NULL);
199     assert (self->encrypted != NULL);
200 
201     /* -----
202      * the re-encrypted file will be the same size as the
203      * previously encrypted file and we have the same understanding
204      * about knowing the size
205      */
206     rc = KFileSize (self->encrypted, &z);
207 
208     if (rc == 0)
209     {
210         uint64_t bid = PlaintextSize_to_BlockCount ( z, NULL );
211         *size = BlockId_to_CiphertextOffset ( bid ) + sizeof ( KEncFileFooter );
212     }
213     else
214     {
215         *size = z;
216     }
217     return rc;
218 }
219 
220 
221 /* ----------------------------------------------------------------------
222  * SetSize
223  *  sets size in bytes of file
224  *
225  *  "size" [ IN ] - new file size
226  */
227 static
KReencFileSetSizeUnsupported(KReencFile * self,uint64_t size)228 rc_t CC KReencFileSetSizeUnsupported (KReencFile *self, uint64_t size)
229 {
230     assert (self);
231 
232     return RC ( rcFS, rcFile, rcUpdating, rcFunction, rcUnsupported );
233 }
234 
235 
236 /*
237  * The next three functions do the actual Out from a KFileRead.
238  * We will only out from the header, or a footer or a single
239  * encrypted block rather than try to fully satisfy all of a
240  * KFileRead. USe KFileReadAll to get more than one part
241  *
242  * For all we have an offset within the part which is the
243  * position from the original read request for the header
244  */
245 static __inline__
KReencFileReadHeaderOut(KReencFile * self,size_t offset,void * buffer,size_t bsize,size_t * num_read)246 rc_t KReencFileReadHeaderOut (KReencFile * self, size_t offset, void * buffer,
247                               size_t bsize, size_t *num_read)
248 {
249     assert (self);
250     assert (offset < sizeof (KEncFileHeader));
251     assert (buffer);
252     assert (bsize);
253     assert (num_read);
254 
255     /* trim request if necessary */
256     if (offset + bsize > sizeof (KEncFileHeader))
257         bsize = sizeof (KEncFileHeader) - offset;
258 
259     memmove (buffer, self->block.text + offset, bsize);
260     self->block_id = NO_CURRENT_BLOCK;
261     *num_read = bsize;
262 
263     return 0;
264 }
265 
266 
267 static __inline__
KReencFileReadBlockOut(KReencFile * self,size_t offset,void * buffer,size_t bsize,size_t * num_read)268 rc_t KReencFileReadBlockOut (KReencFile * self, size_t offset, void * buffer,
269                              size_t bsize, size_t * num_read)
270 {
271     assert (self);
272     assert (offset < sizeof self->block);
273     assert (buffer);
274     assert (bsize);
275     assert (num_read);
276 
277     if (offset + bsize > sizeof self->block)
278         bsize = sizeof self->block - offset;
279 
280     memmove (buffer, self->block.text + offset, bsize);
281     *num_read = bsize;
282 
283     return 0;
284 }
285 
286 
287 static __inline__
KReencFileReadFooterOut(KReencFile * self,size_t offset,void * buffer,size_t bsize,size_t * num_read)288 rc_t KReencFileReadFooterOut (KReencFile * self, size_t offset,
289                               void * buffer, size_t bsize, size_t * num_read)
290 {
291     /* for the footer we will copy out from the footer in self */
292 
293     assert (self);
294     assert (offset < sizeof self->foot);
295     assert (buffer);
296     assert (bsize);
297     assert (num_read);
298 
299 /* KOutMsg ("%s: offset '%zu' bsize '%zu'",__func__,offset,bsize); */
300     if (offset + bsize > sizeof self->foot)
301         bsize = sizeof self->foot - offset;
302 
303     memmove (buffer, self->foot.text + offset, bsize);
304     self->block_id = NO_CURRENT_BLOCK;
305     *num_read = bsize;
306 
307 /* KOutMsg (" *num_read '%zu'\n",bsize); */
308 /* { */
309 /*     size_t ix; */
310 /*     char * b = buffer; */
311 /*     KOutMsg ("%s:",__func__); */
312 /*     for (ix = 0; ix < bsize; ++ix) */
313 /*         KOutMsg (" %2.2x",b[ix]); */
314 /*     KOutMsg ("\n"); */
315 
316 /* } */
317     return 0;
318 }
319 
320 
321 /*
322  * Handle Read within the Encrypted file header
323  *
324  * We use a private interface into the KEncFile then
325  * the Out function below to take what we write with
326  * the Encryptor and copy it to the callers read buffer.
327  */
328 static
329 rc_t KReencFileReadHandleBlock (KReencFile *self,
330                                 uint64_t pos,
331                                 void *buffer,
332                                 size_t bsize,
333                                 size_t *num_read);
334 
335 static __inline__
KReencFileReadHandleHeader(KReencFile * self,uint64_t pos,void * buffer,size_t bsize,size_t * num_read)336 rc_t KReencFileReadHandleHeader (KReencFile *self,
337                                  uint64_t pos,
338                                  void *buffer,
339                                  size_t bsize,
340                                  size_t *num_read)
341 {
342     rc_t rc;
343 
344     assert (self);
345     assert (pos < sizeof (KEncFileHeader));
346     assert (buffer);
347     assert (bsize);
348     assert (num_read);
349 
350     if ( !self->known_size || self->size != sizeof(KEncFileHeader) + sizeof(KEncFileFooter) )
351     {
352         /* added to support NCBInenc and NCBIsenc variants of KEncFile
353          * read the first block of the source file but don't write any of it
354          *
355          * If the source is stream mode only then the next block to be read is the
356          * first and this will not violate by seeking. It will just have already
357          * read the block it will want to read real soon. Likewith within a
358          * single call to KFileReadAll.
359          */
360         rc = KReencFileReadHandleBlock (self, 0, NULL, 0, NULL);
361         if (rc)
362             return rc;
363     }
364 
365     /* use a private function from KEncFile to generate a header */
366     rc = KEncFileWriteHeader (self->enc);
367     if (rc)
368         LOGERR (klogErr, rc, "re-enc error generating encrypted header");
369     else
370     {
371         /*
372          * assume it worked and its the right size
373          * copy the requested portion of the header out to finish the read
374          */
375         rc = KReencFileReadHeaderOut (self, pos, buffer, bsize, num_read);
376         if (rc)
377             LOGERR (klogErr, rc, "re-enc error filling read request");
378     }
379     return rc;
380 }
381 
382 
383 /*
384  * Read the requested block form the source encrypted file
385  */
386 static
KReencFileReadABlock(KReencFile * self,uint64_t block_id)387 rc_t KReencFileReadABlock (KReencFile * self, uint64_t block_id)
388 {
389     rc_t rc;
390 
391 
392 /*     OUTMSG (("%s: block_id %lu\n",__func__,block_id)); */
393 
394     if (block_id + 1 == self->footer_block)
395         memset (self->plain_text, 0, sizeof self->plain_text);
396 
397     /* -----
398      * simple call down to the decryptor to get the plain text data
399      * for this block. We will regenerate the framing when we re-encrypt
400      */
401     rc = KFileReadAll (self->dec, BlockId_to_PlaintextOffset ( block_id ),
402                        &self->plain_text, sizeof (self->plain_text),
403                        &self->num_read);
404     if (rc)
405         LOGERR (klogErr, rc, "re-enc error reading a block");
406 
407     /*
408      * interpret bytes read for validity
409      *
410      * zero means we hit a premature end of file where we expected a block
411      */
412     else if (self->num_read == 0)
413     {
414         /*misleading RC? */
415         rc = RC (rcKrypto, rcFile, rcReading, rcSize, rcInsufficient);
416         LOGERR (klogErr, rc, "re-enc no block read");
417     }
418     /*
419      * less than a full block must be in the last block
420      */
421     else if (self->num_read < sizeof self->plain_text)
422     {
423 /*         KOutMsg ("%s: block_id '%lu'num_read '%zu' of '%zu' last_block '%lu'\n", */
424 /*                  __func__, block_id, self->num_read, sizeof self->plain_text, */
425 /*                  self->footer_block); */
426 
427         if (block_id + 1 != self->footer_block)
428         {
429             rc = RC (rcKrypto, rcFile, rcReading, rcSize, rcInsufficient);
430             LOGERR (klogErr, rc, "re-enc incomplete block read");
431         }
432     }
433     /*
434      * unlikely scenario, read too much
435      */
436     else if (self->num_read > sizeof self->plain_text)
437     {
438         rc = RC (rcKrypto, rcFile, rcReading, rcBuffer, rcIncorrect);
439         LOGERR (klogErr, rc, "re-enc no block read");
440     }
441     if (rc == 0)
442         self->block_id = block_id;
443     return rc;
444 }
445 
446 
447 static
KReencFileWriteABlock(KReencFile * self,uint64_t block_id)448 rc_t KReencFileWriteABlock (KReencFile * self, uint64_t block_id)
449 {
450     rc_t rc;
451 
452     /* -----
453      * simple call to encrypt an entire data section for the relevant block
454      * We send in up to 32KB of plain text which through two element KFiles will
455      * we written into a buffer back in this KFile. More data will be written
456      * to that buffer than requested here - that is the framing and also the
457      * header if the block is the first one.
458      */
459     rc = KFileWriteAll (self->enc, BlockId_to_PlaintextOffset ( block_id ),
460                          self->plain_text, self->num_read, &self->num_writ);
461 
462     if (rc)
463         LOGERR (klogInt, rc, "re-enc error encrypting a block");
464 
465     else if (self->num_writ != self->num_read)
466     {
467         rc = RC (rcKrypto, rcFile, rcWriting, rcFile, rcIncomplete);
468         LOGERR (klogErr, rc, "re-enc failure encrypting all of block");
469     }
470 
471     /* trigger a flush */
472 /*     if (rc == 0) */
473 /*         rc = KFileWriteAll (self->enc, BlockId_to_DecryptedPos (block_id^1), */
474 /*                          self->plain_text, self->num_read, &self->num_writ); */
475 
476     return rc;
477 }
478 
479 
480 /*
481  * Add the current encrypted block to the footer statistics
482  */
483 static __inline__
KReencFileAddToFooter(KReencFile * self)484 rc_t KReencFileAddToFooter (KReencFile * self)
485 {
486     assert (self);
487 /*     KOutMsg ("%s: ",__func__);  */
488 
489     if (self->block.block.crc != self->block.block.crc_copy)
490     {
491         rc_t rc = RC (rcKrypto, rcFile, rcReading, rcChecksum, rcInvalid);
492         LOGERR (klogInt, rc, "rc-enc block CRCs disagree");
493         return rc;
494     }
495 
496     ++ self->foot.foot.block_count;
497     self->foot.foot.crc_checksum += self->block.block.crc;
498 
499 /*     KOutMsg ("%lu %lu %lu\n",self->foot.foot.block_count,self->block.block.crc,self->foot.foot.crc_checksum); */
500     return 0;
501 }
502 
503 
504 /*
505  * Read a block from the source encrypted block and Write it which reencrypts it
506  *
507  * The new_block parameter says whether this is the first time we've seen this
508  * block. If it is we need to add data to the footer
509  */
510 static
KReencFileReencBlock(KReencFile * self,uint64_t block_id,bool new_block)511 rc_t KReencFileReencBlock (KReencFile * self, uint64_t block_id, bool new_block)
512 {
513     rc_t rc;
514 
515     assert (self);
516 
517 /*     KOutMsg ("%s: %lu %lu\n", __func__, block_id, self->footer_block); */
518     assert (block_id <= self->footer_block);
519     assert ((new_block == true) || (new_block == false));
520 
521     rc = KReencFileReadABlock (self, block_id);
522     if (rc)
523         LOGERR (klogErr, rc, "re-enc failure to read a block");
524     else
525     {
526         if ((self->num_read == 0) || (self->num_read > sizeof (self->plain_text)))
527         {
528             rc = RC (rcFS, rcFile, rcReading, rcSize, rcIncorrect);
529             LOGERR (klogErr, rc, "Bad length on block read of encrypted file");
530         }
531         else
532         {
533             rc = KReencFileWriteABlock (self, block_id);
534             if (rc)
535                 LOGERR (klogErr, rc, "re-enc failure to write a block");
536 
537             else if (new_block)
538             {
539                 rc = KReencFileAddToFooter (self);
540                 if (rc)
541                     LOGERR (klogErr, rc,
542                             "re-enc failure to do block accounting");
543             }
544         }
545     }
546     return rc;
547 }
548 
549 
550 /*
551  * Handle Read within the Encrypted file footer
552  */
553 static __inline__
KReencFileReadHandleFooter(KReencFile * self,uint64_t pos,void * buffer,size_t bsize,size_t * num_read)554 rc_t KReencFileReadHandleFooter (KReencFile *self,
555                                  uint64_t pos,
556                                  void *buffer,
557                                  size_t bsize,
558                                  size_t *num_read)
559 {
560     uint64_t block_id;  /* block id for the footer gets us the start of footer */
561     size_t offset;
562     rc_t rc;
563 
564 /* KOutMsg ("\n\n\n%s: pos '%lu' bsize '%zu'\n",__func__,pos,bsize); */
565 
566     assert (self);
567     assert (pos >= sizeof (KEncFileHeader));
568     assert (buffer);
569     assert (bsize);
570     assert (num_read);
571 
572     rc = 0;
573     block_id = EncryptedPos_to_BlockId (pos, NULL, NULL);
574 
575     assert (block_id == self->footer_block);
576 
577     offset = pos - BlockId_to_CiphertextOffset ( block_id );
578 
579     assert (offset < sizeof self->foot);
580 
581     /* if we are tying to treat this as a footer but it wasn't the next
582      * expected block mark all inbetween as missing to handle in the
583      * function just below this
584      */
585 
586 /* KOutMsg ("%s: self->next_block %lu\n",__func__, self->next_block); */
587 
588     self->foot.foot.block_count = block_id;
589     self->foot.foot.crc_checksum = 0;
590 
591     if (rc == 0)
592     {
593         uint64_t header_pos;
594 
595         header_pos = BlockId_to_CiphertextOffset ( block_id );
596 
597         assert (header_pos <= pos);
598         assert (pos - header_pos <= sizeof self->foot);
599 
600         rc = KReencFileReadFooterOut (self, (size_t)(pos - header_pos),
601                                       buffer, bsize, num_read);
602 
603 /*             KOutMsg ("%s: footer '%lu' '%lx'\n",__func__, */
604 /*                      self->foot.foot.block_count, */
605 /*                      self->foot.foot.crc_checksum); */
606 
607         if (rc)
608             LOGERR (klogInt, rc, "re-enc failed to output footer");
609     }
610     return rc;
611 }
612 
613 
614 /*
615  *
616  */
617 static
KReencFileReadHandleBlock(KReencFile * self,uint64_t pos,void * buffer,size_t bsize,size_t * num_read)618 rc_t KReencFileReadHandleBlock (KReencFile *self,
619                                 uint64_t pos,
620                                 void *buffer,
621                                 size_t bsize,
622                                 size_t *num_read)
623 {
624     rc_t rc = 0;            /* we have a path where we need to check this without set */
625     uint64_t block_id;      /* block id for the requeted position */
626     uint32_t offset;        /* how far into the encrypted block */
627     bool new_block;         /* is this the first time for this block */
628 
629     /* -----
630      * figure out what block this corresponds to.
631      * the header is for this purpose part of the first block
632      * when we decide what to give the reader. We only care
633      * about which block and not whether it is in the payload
634      * or framing.
635      * This block id is not to a known to exist block. It could be
636      * to the header, the footer or past the end of the file.
637      *
638      * NOTE: This could be a pre-fetch of the first block when
639      * processing the header - it will have some funny values
640      * with just a self and all other parameters 0s.
641      * pos of zero gives block_id of 0 and the bsize of zero
642      * means we never look at the others. Thi sis to allow the
643      * (at the time this was written) new feature of two
644      * different file-signatures for encrypted files.
645      */
646     block_id = EncryptedPos_to_BlockId (pos, NULL, NULL);
647 
648     if (block_id != self->block_id)
649     {
650         new_block = true;
651 
652         if (rc == 0)
653         {
654             /* read requested block */
655             rc = KReencFileReencBlock (self, block_id, new_block);
656             if (rc)
657             {
658                 LOGERR (klogErr, rc,
659                         "re-enc failure re-encryptinng a requested block");
660             }
661         }
662     }
663     if ((rc == 0) && (bsize > 0))
664     {
665         /* satisfy read request
666          *
667          * if we are here we decrypted and re-encrypted the
668          * expected block
669          */
670         offset = ( uint32_t ) ( pos - BlockId_to_CiphertextOffset ( block_id ) );
671         rc = KReencFileReadBlockOut (self, offset, buffer, bsize, num_read);
672         if (rc)
673             LOGERR (klogErr, rc, "re-enc error copying out from block");
674     }
675     return rc;
676 }
677 
678 
679 /* ----------------------------------------------------------------------
680  * Read
681  *  read file from known position
682  *
683  *  "pos" [ IN ] - starting position within file
684  *
685  *  "buffer" [ OUT ] and "bsize" [ IN ] - return buffer for read
686  *
687  *  "num_read" [ OUT, NULL OKAY ] - optional return parameter
688  *  giving number of bytes actually read
689  */
690 #if 0
691 static
692 rc_t CC KReencFileReadUnsupported (const KReencFile *self,
693                                    uint64_t pos,
694                                    void *buffer,
695                                    size_t bsize,
696                                    size_t *num_read)
697 {
698     assert (self);
699     assert (buffer);
700     assert (bsize);
701     assert (num_read);
702 
703     return RC ( rcFS, rcFile, rcReading, rcFunction, rcUnsupported );
704 }
705 #endif
706 
707 /*
708  * Read will often return only a partial read.
709  *
710  * We take the less complex route here and return only from the header, a
711  * single block or the footer, whatever is at the beginning of the requested
712  * region.
713  */
714 static
KReencFileRead(const KReencFile * self_,uint64_t pos,void * buffer,size_t bsize,size_t * num_read)715 rc_t CC KReencFileRead (const KReencFile *self_,
716                         uint64_t pos,
717                         void *buffer,
718                         size_t bsize,
719                         size_t *num_read)
720 {
721     /* make it all mutable and stop using self_ */
722     KReencFile * self = (KReencFile *)self_;
723     rc_t rc = 0;
724 
725     assert (self);
726     assert (buffer);
727     assert (bsize);
728     assert (num_read);
729 
730     *num_read = 0;
731 
732     /* -----
733      * the size of the re-encrypted file will be the same as the size of the
734      * previously encrypted file so we can bail early if we know we can.
735      *
736      * The initial use case is that the file will just be lying out on disk
737      * and a KSysfile so that we do know the size.
738      *
739      * There are three pieces to an encrypted file:
740      * 1 Header
741      * 2 One or More Blocks
742      * 3 Footer
743      *
744      * We will only know if we are in the footer if we already know the size of
745      * the file. Else we have to find the footer by not finding a block when we
746      * try to read it. We'll thus look for the header first, then the footer else
747      * we try to find a block,
748      */
749 
750     /* Header */
751     if (pos < sizeof (KEncFileHeader))
752         rc = KReencFileReadHandleHeader (self, pos, buffer, bsize, num_read);
753 
754     /* if past the whole encrypted file */
755     else if (pos >= self->size)
756         rc = 0;
757 
758     /* Footer */
759     else if (pos >= self->size - sizeof self->foot)
760         rc = KReencFileReadHandleFooter (self, pos, buffer, bsize, num_read);
761 
762     /* Blocks */
763     else
764         rc = KReencFileReadHandleBlock (self, pos, buffer, bsize, num_read);
765 
766     return rc;
767 }
768 
769 
770 /* ----------------------------------------------------------------------
771  * Write
772  *  write file at known position
773  *
774  *  "pos" [ IN ] - starting position within file
775  *  "buffer" [ IN ] and "size" [ IN ] - data to be written
776  *
777  *  "num_writ" [ OUT, NULL OKAY ] - optional return parameter
778  *  giving number of bytes actually written
779  *
780  * Unsupported as we now treat archives as READ ONLY
781  */
782 static
KReencFileWriteUnsupported(KReencFile * self,uint64_t pos,const void * buffer,size_t bsize,size_t * num_writ)783 rc_t CC KReencFileWriteUnsupported (KReencFile *self, uint64_t pos,
784                                     const void *buffer, size_t bsize,
785                                     size_t *num_writ)
786 {
787     rc_t rc = RC (rcFS, rcFile, rcReading, rcFunction, rcUnsupported);
788 
789     assert (self);
790     assert (buffer);
791     assert (bsize);
792     assert (num_writ);
793 
794     assert (false);
795 
796     LOGERR (klogInt, rc, "KFileRead failed to filter call");
797 
798     return rc;
799 }
800 
801 #if 0
802 static
803 rc_t CC KReencFileWrite (KReencFile *self, uint64_t pos,
804                          const void *buffer, size_t bsize,
805                          size_t *num_writ)
806 {
807     assert (self);
808     assert (buffer);
809     assert (bsize);
810     assert (num_writ);
811 
812     assert (REENCFILE_WRITE_SUPPORTED);
813 
814     /* this needs to be finished before we can support Write open */
815 
816     return KReencFileWriteUnsupported (self, pos, buffer, bsize, num_writ);
817 }
818 #endif
819 
820 
821 /* ----------------------------------------------------------------------
822  * Type
823  *  returns a KFileDesc
824  *  not intended to be a content type,
825  *  but rather an implementation class
826  */
827 static
KReencFileType(const KReencFile * self)828 uint32_t CC KReencFileType (const KReencFile *self)
829 {
830     assert (self != NULL);
831     assert (self->encrypted != NULL);
832 
833     return KFileType (self->encrypted);
834 }
835 
836 
837 /* ----------------------------------------------------------------------
838  * KReencFileMake
839  *  create a new file object
840  */
841 
842 
843 /* ----------
844  * MakeParamValidate
845  * common parameter validation for both reencryptors
846  */
847 static
KReencFileMakeParamValidate(const KFile ** pself,const KFile * encrypted,const KKey * deckey,const KKey * enckey)848 rc_t KReencFileMakeParamValidate (const KFile ** pself, const KFile * encrypted,
849                                   const KKey * deckey, const KKey * enckey)
850 {
851     rc_t rc = 0;
852 
853     do
854     {
855         if (pself == NULL)
856         {
857             rc = RC (rcFS, rcFile, rcConstructing, rcSelf, rcNull);
858             LOGERR (klogErr, rc,
859                     "pointer to self NULL when creating "
860                     "a re-encryptor");
861             break;
862         }
863 
864         *pself = NULL;
865 
866         if (encrypted == NULL)
867         {
868             rc = RC (rcFS, rcFile, rcConstructing, rcParam, rcNull);
869             LOGERR (klogErr, rc,
870                     "encrypted file not supplied when creating "
871                     "an encryptor/decryptor");
872             break;
873         }
874 
875         if ((enckey == NULL) || (deckey == NULL))
876         {
877             rc = RC (rcFS, rcFile, rcConstructing, rcParam, rcNull);
878             LOGERR (klogErr, rc,
879                     "key not supplied when creating a re-encryptor");
880             break;
881         }
882 
883         switch (deckey->type)
884         {
885         default:
886             rc = RC (rcFS, rcFile, rcConstructing, rcParam, rcInvalid);
887             PLOGERR (klogErr,
888                      (klogErr, rc, "invalid key type '$(T)' should be "
889                       "kkeyAES128(1), kkeyAES192(2) or kkeyAES256(3)",
890                       "T=%u", deckey->type));
891             break;
892 
893         case kkeyAES128:
894         case kkeyAES192:
895         case kkeyAES256:
896             break;
897         }
898         switch (enckey->type)
899         {
900         default:
901             rc = RC (rcFS, rcFile, rcConstructing, rcParam, rcInvalid);
902             PLOGERR (klogErr,
903                      (klogErr, rc, "invalid key type '$(T)' should be "
904                       "kkeyAES128(1), kkeyAES192(2) or kkeyAES256(3)",
905                       "T=%u", enckey->type));
906             break;
907 
908         case kkeyAES128:
909         case kkeyAES192:
910         case kkeyAES256:
911             break;
912         }
913     } while (0);
914     return rc;
915 }
916 
917 
918 /* ----------
919  * Read mode is fully seekable if the underlying KFile is seekable some
920  * integrity checking will not be performed in allowing this seeking.
921  */
922 static const KFile_vt_v1 vtKReencFileRead =
923 {
924     /* version */
925     1, 1,
926 
927     /* 1.0 */
928     KReencFileDestroy,
929     KReencFileGetSysFileUnsupported,
930     KReencFileRandomAccess,
931     KReencFileSize,
932     KReencFileSetSizeUnsupported,
933     KReencFileRead,
934     KReencFileWriteUnsupported,
935 
936     /* 1.1 */
937     KReencFileType
938 };
939 
940 
KReencFileMakeRead(const KFile ** pself,const KFile * encrypted,const KKey * deckey,const KKey * enckey)941 LIB_EXPORT rc_t CC KReencFileMakeRead (const KFile ** pself,
942                                        const KFile * encrypted,
943                                        const KKey * deckey,
944                                        const KKey * enckey)
945 {
946     KReencFile * self;
947     uint64_t size;
948     uint64_t block_count;
949     rc_t rc;
950 
951     rc = KReencFileMakeParamValidate (pself, encrypted, deckey, enckey);
952     if (rc)
953     {
954         LOGERR (klogErr, rc, "error constructing decryptor");
955         return rc;
956     }
957 
958     rc = KFileSize (encrypted, &size);
959     if (GetRCState (rc) == rcUnsupported)
960     {
961         size = 0;
962         rc = RC (rcKrypto, rcFile, rcConstructing, rcSize, rcUnsupported);
963         LOGERR (klogErr, rc, "Can't re-encrypt files that don't support KFileSize");
964         return rc;
965     }
966 
967     if (rc)
968     {
969         LOGERR (klogErr, rc, "Unable to attempt to size encrypted file for reencryption");
970         return rc;
971     }
972 
973     rc = KFileAddRef (encrypted);
974     if (rc)
975     {
976         LOGERR (klogErr, rc, "Unable to add reference to encrypted file for re-encryptor");
977         return rc;
978     }
979 
980     if (size == 0)
981     {
982         *pself = encrypted;
983         return rc;
984     }
985     if (size < sizeof (KEncFileHeader) + sizeof (KEncFileFooter))
986     {
987         rc = RC (rcKrypto, rcFile, rcConstructing, rcSize, rcInvalid);
988         LOGERR (klogErr, rc, "encrypted file too short to be valied for re-encryption");
989         KFileRelease (encrypted);
990         return rc;
991     }
992 
993     {
994         uint64_t temp;
995 
996         temp = size - (sizeof (KEncFileHeader) + sizeof (KEncFileFooter));
997         block_count = temp / sizeof (KEncFileBlock);
998         if ((block_count * sizeof (KEncFileBlock)) != temp)
999         {
1000             rc = RC (rcKrypto, rcFile, rcConstructing, rcSize, rcInvalid);
1001             LOGERR (klogErr, rc, "encrypted file invalid size for re-encryption");
1002             KFileRelease (encrypted);
1003             return rc;
1004         }
1005     }
1006 
1007     self = calloc (1,sizeof (*self));
1008 
1009     if (self == NULL)
1010     {
1011         rc = RC (rcFS, rcFile, rcConstructing, rcMemory, rcExhausted);
1012         LOGERR (klogSys, rc,
1013                 "out of memory creating encrypter and/or decryptor");
1014     }
1015     else
1016     {
1017         rc = KFileInit (&self->dad, (const KFile_vt*)&vtKReencFileRead, "KReencFile", "no-name", true, false);
1018         if (rc)
1019             LOGERR (klogInt, rc, "failed in initialize reenc base class");
1020         else
1021         {
1022             self->encrypted = encrypted;
1023             /* dec, enc, ram need to be Make */
1024             /* num_read, num_write stay 0 */
1025             self->block_id = NO_CURRENT_BLOCK;
1026             /* missing needs to be Made */
1027             /* next_block stays 0 */
1028             /* seek_block stays 0 - obsolete */
1029             self->footer_block = EncryptedPos_to_BlockId (size, NULL, NULL);
1030             self->size = size;
1031             self->known_size = true; /* obsolete */
1032             /* plain_text and block stay 0 */
1033             self->foot.foot.block_count = self->footer_block;
1034 
1035             rc = KEncFileMakeRead (&self->dec, encrypted, deckey);
1036             if (rc)
1037                 LOGERR (klogErr, rc, "Failed to create re-enc decryptor");
1038 
1039             else
1040             {
1041                 rc = KRamFileMakeUpdate (&self->ram, self->block.text,
1042                                          sizeof self->block.text);
1043                 if (rc)
1044                     LOGERR (klogErr, rc,
1045                             "Failed to create re-enc encryptor");
1046                 else
1047                 {
1048 
1049                     rc = KEncFileMakeWriteBlock (&self->enc, self->ram, enckey);
1050                     if (rc)
1051                         LOGERR (klogErr, rc,
1052                                 "Failed to create RAM file for reenc");
1053                     else
1054                     {
1055                         *pself = &self->dad;
1056                         return 0;
1057                     }
1058                     KFileRelease (self->ram);
1059                 }
1060                 KFileRelease (self->dec);
1061             }
1062         }
1063         free (self);
1064     }
1065     KFileRelease (encrypted);
1066     return rc;
1067 }
1068 
1069 
1070 static const KFile_vt_v1 vtKEncryptFileRead =
1071 {
1072     /* version */
1073     1, 1,
1074 
1075     /* 1.0 */
1076     KReencFileDestroy,
1077     KReencFileGetSysFileUnsupported,
1078     KReencFileRandomAccess,
1079     KEncryptFileSize,
1080     KReencFileSetSizeUnsupported,
1081     KReencFileRead,
1082     KReencFileWriteUnsupported,
1083 
1084     /* 1.1 */
1085     KReencFileType
1086 };
1087 
1088 
KEncryptFileMakeRead(const KFile ** pself,const KFile * encrypted,const KKey * enckey)1089 LIB_EXPORT rc_t CC KEncryptFileMakeRead (const KFile ** pself,
1090                                          const KFile * encrypted,
1091                                          const KKey * enckey)
1092 {
1093     KReencFile * self;
1094     uint64_t rawsize;
1095     uint64_t size;
1096     rc_t rc;
1097 
1098     rc = KReencFileMakeParamValidate (pself, encrypted, enckey, enckey);
1099     if (rc)
1100     {
1101         LOGERR (klogErr, rc, "error constructing decryptor");
1102         return rc;
1103     }
1104 
1105     rc = KFileSize (encrypted, &rawsize);
1106     if (GetRCState (rc) == rcUnsupported)
1107     {
1108         size = 0;
1109         rc = RC (rcKrypto, rcFile, rcConstructing, rcSize, rcUnsupported);
1110         LOGERR (klogErr, rc, "Can't encrypt files that don't support KFileSize");
1111         return rc;
1112     }
1113 
1114     if (rc)
1115     {
1116         LOGERR (klogErr, rc, "Unable to attempt to size encrypted file for encryption");
1117         return rc;
1118     }
1119 
1120     rc = KFileAddRef (encrypted);
1121     if (rc)
1122     {
1123         LOGERR (klogErr, rc, "Unable to add reference to unencrypted file for encryptor");
1124         return rc;
1125     }
1126 
1127     self = calloc (1,sizeof (*self));
1128 
1129     if (self == NULL)
1130     {
1131         rc = RC (rcFS, rcFile, rcConstructing, rcMemory, rcExhausted);
1132         LOGERR (klogSys, rc,
1133                 "out of memory creating encrypter and/or decryptor");
1134     }
1135     else
1136     {
1137         rc = KFileInit (&self->dad, (const KFile_vt*)&vtKEncryptFileRead, "KEncryptFile", "no-path", true, false);
1138         if (rc)
1139             LOGERR (klogInt, rc, "failed in initialize reenc base class");
1140         else
1141         {
1142             self->encrypted = encrypted;
1143             /* dec, enc, ram need to be made below */
1144             /* num_read, num_write stay 0 */
1145             self->block_id = NO_CURRENT_BLOCK;
1146             /* missing needs to be Made */
1147             /* next_block stays 0 */
1148             /* seek_block stays 0 - obsolete */
1149 
1150             self->footer_block = PlaintextSize_to_BlockCount ( rawsize, NULL );
1151             size = BlockId_to_CiphertextOffset ( self -> footer_block ) + sizeof ( KEncFileFooter );
1152             self->size = size;
1153             self->known_size = true; /* obsolete */
1154 
1155             /* plain_text and block stay 0 */
1156             self->foot.foot.block_count = self->footer_block;
1157 
1158             rc = KFileAddRef (self->dec = encrypted);
1159             if (rc)
1160                 LOGERR (klogErr, rc, "Unable to add reference to unencrypted file for encryptor");
1161 
1162             else
1163             {
1164                 rc = KRamFileMakeUpdate (&self->ram, self->block.text,
1165                                          sizeof self->block.text);
1166                 if (rc)
1167                     LOGERR (klogErr, rc,
1168                             "Failed to create ram file for encryptor");
1169                 else
1170                 {
1171 
1172                     rc = KEncFileMakeWriteBlock (&self->enc, self->ram, enckey);
1173                     if (rc)
1174                         LOGERR (klogErr, rc,
1175                                 "Failed to create RAM file for enc");
1176                     else
1177                     {
1178                         *pself = &self->dad;
1179                         return 0;
1180                     }
1181                     KFileRelease (self->ram);
1182                 }
1183                 KFileRelease (self->dec);
1184             }
1185         }
1186         free (self);
1187     }
1188     KFileRelease (encrypted);
1189     return rc;
1190 }
1191 
1192 
1193 /* ----------
1194  * Write mode re-encrypted file
1195  */
1196 #if 0
1197 static const KFile_vt_v1 vtKReencFileWrite =
1198 {
1199     /* version */
1200     1, 1,
1201 
1202     /* 1.0 */
1203     KReencFileDestroy,
1204     KReencFileGetSysFileUnsupported,
1205     KReencFileRandomAccess,
1206     KReencFileSize,
1207     KReencFileSetSizeUnsupported,
1208     KReencFileReadUnsupported,
1209     KReencFileWrite,
1210 
1211     /* 1.1 */
1212     KReencFileType
1213 };
1214 #endif
1215 
KReencFileMakeWrite(KFile ** pself,KFile * encrypted,const KKey * deckey,const KKey * enckey)1216 LIB_EXPORT rc_t CC KReencFileMakeWrite (KFile ** pself,
1217                                         KFile * encrypted,
1218                                         const KKey * deckey,
1219                                         const KKey * enckey)
1220 {
1221 #if REENCFILE_WRITE_SUPPORTED
1222     KReencFile * self;
1223     rc_t rc;
1224 
1225     rc = KFileSetSize (encrypted, 0);
1226 #if 0
1227     if (rc)
1228         LOGERR (klogWarn, rc, "error truncating output file - "
1229                 "corrupted file might result");
1230 #endif
1231 
1232     rc = KReencFileMakeParamValidate (&self, encrypted, deckey, enckey,
1233                                       &vtKEncFileWrite, true);
1234     if (rc)
1235         LOGERR (klogErr, rc, "error creating encryptor");
1236     else
1237         *pself = &self->dad;
1238     return rc;
1239 #else
1240     return RC ( rcFS, rcFile, rcCreating, rcFunction, rcUnsupported );
1241 #endif
1242 }
1243 
1244 
1245 /* end of file reencfile.c */
1246 
1247 
1248