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/encfile.h>
27 #include <krypto/encfile-priv.h>
28 #include "encfile-priv.h"
29 #include <krypto/cipher.h>
30 #include <krypto/ciphermgr.h>
31 #include <krypto/key.h>
32 
33 /* #include "aes-priv.h" */
34 
35 #include <klib/rc.h>
36 #include <klib/checksum.h>
37 #include <klib/log.h>
38 #include <klib/out.h>
39 #include <klib/debug.h>
40 #include <klib/status.h>
41 /* #include <klib/vector.h> */
42 /* #include <klib/status.h> */
43 #include <kfs/file.h>
44 #include <kfs/sra.h>
45 #include <sysalloc.h>
46 
47 #include <byteswap.h>
48 #include <stdlib.h>
49 #include <string.h>
50 #include <assert.h>
51 /* #include <klib/out.h> */
52 
53 
54 /* ----------------------------------------------------------------------
55  * KEncFile
56  *   Base object class for the encryption, decryption and validation of
57  *   the file format defined above
58  */
59 
60 #define KFILE_IMPL struct KEncFile
61 #include <kfs/impl.h>
62 
63 
64 #include "encfile-priv.h"
65 
66 typedef struct KEncFileCiphers KEncFileCiphers;
67 struct KEncFileCiphers
68 {
69     KCipher * master;
70     KCipher * block;
71 };
72 
73 
74 typedef struct KEncFileIVec { uint8_t ivec [16]; } KEncFileIVec;
75 
76 /* -----
77  */
78 struct KEncFile
79 {
80     KFile dad;                  /* base class */
81     KFile * encrypted;          /* encrypted file as a KFile */
82     KEncFileCiphers ciphers;    /* file and block ciphers */
83     KEncFileBlock block;        /* current data block */
84     KEncFileFooter foot;        /* contains crc checksum and block count */
85     uint64_t dec_size;          /* size of decrypted file */
86     uint64_t enc_size;          /* size of encrypted file */
87     bool dirty;                 /* data written but not flushed set in Write cleared in Flush*/
88     bool seekable;              /* we can seek within the encrypted file */
89     bool size_known;            /* can we know the size? Only streaming read can not know */
90     bool bswap;                 /* file created on system of opposite endianess */
91     bool changed;               /* some write has happened cleared in Make, set in BufferWrite */
92     bool sought;                /* did a seek on a read or write invalidating crc checksum */
93     bool has_header;            /* have we read or written a header? */
94     bool eof;
95     bool sra;                   /* we know we are encrypting an SRA/KAR archive file */
96     bool swarm;                 /* block mode for swarm mode using KReencFile or KEncryptFile */
97     KEncFileVersion version;    /* version from the header if read; or the one being written */
98 };
99 
100 
101 /* ----------
102  * BufferCalcMD5
103  *    Generate the MD5 digest for a buffer
104  */
105 static __inline__
BufferCalcMD5(const void * buffer,size_t size,uint8_t digest[16])106 void BufferCalcMD5 (const void * buffer, size_t size, uint8_t digest [16])
107 {
108     MD5State state;
109 
110     assert (buffer);
111     assert (size);
112     assert (digest);
113 
114     MD5StateInit (&state);
115     MD5StateAppend (&state, buffer, size);
116     MD5StateFinish (&state, digest);
117 }
118 
119 
120 /* -----
121  * return true or false as to whether the the buffer described is all 0 bits
122  */
123 static __inline__
BufferAllZero(const void * buffer_,size_t size)124 bool BufferAllZero (const void * buffer_, size_t size)
125 {
126     const uint8_t * buffer;
127     size_t count;
128     bool ret;
129 
130     assert (buffer_);
131 
132     buffer = buffer_;
133     ret = true;
134 
135     for (count = 0; count < size; ++count)
136     {
137         if (buffer[count] != '\0')
138         {
139             ret = false;
140             break;
141         }
142     }
143     return ret;
144 }
145 
146 /* Compensate that KFileReadAll became non-blocking.
147    Keep calling KFileReadAll until buffer fills up
148    to satisfy caller's expectations */
KFile_ReadAllBlocking(const KFile * self,uint64_t pos,void * buffer,size_t bsize,size_t * num_read)149 static rc_t KFile_ReadAllBlocking(const KFile *self, uint64_t pos,
150     void *buffer, size_t bsize, size_t *num_read)
151 {
152     size_t count = 0;
153     rc_t rc = KFileReadAll(self, pos, buffer, bsize, &count);
154     size_t total = count;
155 
156     if (rc == 0 && count != 0 && count < bsize) {
157         uint8_t *b = buffer;
158         for (b = buffer; total < bsize; total += count) {
159             count = 0;
160             rc = KFileReadAll(self, pos + total, b + total, bsize - total,
161                 &count);
162             if (rc != 0 || count == 0)
163                 break;
164         }
165     }
166 
167     if (total != 0) {
168         *num_read = total;
169         return 0;
170     }
171 
172     return rc;
173 }
174 
175 /* ----------
176  * BufferRead
177  *    Read from an encrypted file and update size if warranted
178  *
179  * Read's aren't guaranteed to get full amount
180  */
181 static
KEncFileBufferRead(KEncFile * self,uint64_t offset,void * buffer,size_t bsize,size_t * num_read)182 rc_t KEncFileBufferRead (KEncFile * self, uint64_t offset, void * buffer,
183                          size_t bsize, size_t * num_read)
184 {
185     rc_t rc;
186 
187     assert (self);
188     assert (self->encrypted);
189     assert (buffer);
190     assert (bsize > 0);
191     assert (num_read);
192 
193     rc = KFile_ReadAllBlocking (self->encrypted, offset, buffer, bsize,
194         num_read);
195     if (rc == 0)
196     {
197         if (self->enc_size < offset + *num_read)
198         {
199             self->enc_size = offset + *num_read;
200         }
201     }
202 /* leave logging to callers? */
203     else
204         PLOGERR (klogErr, (klogErr, rc, "Error reading from encrypted file "
205                            "at '$(P)", "P=%lu", offset));
206 
207     return rc;
208 }
209 
210 
211 /* ----------
212  * BufferWrite
213  *    write to an encrypted file, mark it as changed and update size if warranted
214  */
215 static
KEncFileBufferWrite(KEncFile * self,uint64_t offset,const void * buffer,size_t bsize,size_t * num_writ)216 rc_t KEncFileBufferWrite (KEncFile * self, uint64_t offset, const void * buffer,
217                           size_t bsize, size_t * num_writ)
218 {
219     rc_t rc = 0;
220 
221     assert (self);
222     assert (self->encrypted);
223     assert (buffer);
224     assert (bsize > 0);
225     assert (num_writ);
226 
227     rc = KFileWriteAll (self->encrypted, offset, buffer, bsize, num_writ);
228     if (rc != 0)
229     {
230 /* leave logging to callers? */
231         PLOGERR (klogErr, (klogErr, rc, "Error writing to encrypted file "
232                            "at '$(P)", "P=%lu", offset));
233         // make sure we don't crash later in destructor
234         if (self->enc_size > offset + *num_writ)
235         {
236             self->enc_size = offset + *num_writ;
237         }
238         return rc;
239     }
240 
241     if (bsize != *num_writ)
242     {
243 /*         KOutMsg ("%s: bsize %zu *num_writ %zu\n",__func__, bsize, *num_writ); */
244         rc = RC (rcKrypto, rcFile, rcWriting, rcTransfer, rcIncomplete);
245 /* leave logging to callers? */
246         PLOGERR (klogErr, (klogErr, rc, "Error writing full buffer to"
247                            " encrypted file at '$(P) wrote '$(W)'",
248                            "P=%lu,W=%zu", offset, *num_writ));
249     }
250     else if (*num_writ > 0)
251     {
252         self->changed = true;
253         if (self->enc_size < offset + *num_writ)
254         {
255             self->enc_size = offset + *num_writ;
256         }
257     }
258     return rc;
259 }
260 
261 
262 /* ----------------------------------------------------------------------
263  * operations on KEncFileHeader
264  *
265  * The header only needs to be read and validated or written  we need not
266  * reatian within the KEncFile object any information about the header in
267  * the initial version of this file format.
268  */
269 
270 /* -----
271  * the first eight bytes of the file are two four byte strings
272  *  The first is a common "NCBI"
273  *  The second is the format specific "nenc"
274  */
275 #if 0
276 static const KEncFileSig KEncFileSignature = "NCBInenc";
277 #endif
278 
279 /* -----
280  * the common constant used throughout the project to check the byte order
281  * as written by the system which created the file
282  */
283 /* enum fails to handle these due to integer overflow */
284 
285 #define eEncFileByteOrderTag     (0x05031988)
286 #define eEncFileByteOrderReverse (0x88190305)
287 
288 /* ----
289  */
290 #define eCurrentVersion        (0x00000002)
291 #define eCurrentVersionReverse (0x02000000)
292 
293 
294 static
295 const KEncFileHeader const_header
296 = { "NCBInenc", eEncFileByteOrderTag, eCurrentVersion };
297 
298 
299 static
300 const KEncFileHeader const_bswap_header
301 = { "NCBInenc", eEncFileByteOrderReverse, eCurrentVersionReverse };
302 
303 /* skipping v1 for NCBIkenc */
304 static
305 const KEncFileHeader const_header_sra
306 = { "NCBIsenc", eEncFileByteOrderTag, eCurrentVersion };
307 
308 
309 #if 0
310 static
311 const KEncFileHeader const_bswap_header_sra
312 = { "NCBIsenc", eEncFileByteOrderReverse, eCurrentVersionReverse };
313 #endif
314 
315 /* ----------
316  * HeaderRead
317  *    Read the header of an encrypted file and validate it.
318  *
319  * We only allow a missing header for an empty file opened for update
320  */
321 static
KEncFileHeaderRead(KEncFile * self)322 rc_t KEncFileHeaderRead (KEncFile * self)
323 {
324     KEncFileHeader header;
325     size_t num_read;
326     rc_t rc;
327 
328     assert (self);
329 
330     rc = KEncFileBufferRead (self, 0, &header, sizeof (header), &num_read);
331     if (rc)
332         LOGERR (klogErr, rc, "error reading encrypted file header");
333 
334     else if ((num_read == 0) && (self->dad.write_enabled))
335     {
336         /* only allow read to fail with zero length if opened with write */
337         self->version = eCurrentVersion;
338         self->bswap = false;
339         self->enc_size = 0; /* redundant? */
340         self->dec_size = 0; /* redundant? */
341     }
342     else if (num_read != sizeof (header))
343     {
344         rc = RC (rcFS, rcFile, rcConstructing, rcHeader, rcTooShort);
345         PLOGERR (klogErr,
346                  (klogErr, rc, "error reading full header of encrypted "
347                   "file  wanted '$(S)' got '$(N); bytes read", "S=%u,N=%u",
348                   sizeof (header), num_read));
349     }
350     else
351     {
352         rc_t orc;
353 
354         if (memcmp (header.file_sig, const_header.file_sig,
355                      sizeof (header.file_sig)) == 0)
356             self->sra = false;
357         else if (memcmp (header.file_sig, const_header_sra.file_sig,
358                          sizeof (header.file_sig)) == 0)
359             self->sra = true;
360         else
361         {
362             rc = RC (rcFS, rcFile, rcConstructing, rcHeader, rcInvalid);
363             LOGERR (klogErr, rc, "file signature not correct for encrypted file");
364         }
365 
366         switch (header.byte_order)
367         {
368         case eEncFileByteOrderTag:
369             self->bswap = false;
370             self->version = header.version;
371             break;
372 
373         case eEncFileByteOrderReverse:
374             self->bswap = true;
375 
376             /* assert for the right bswap call */
377             assert (sizeof (self->version) == 4);
378 
379             self->version = bswap_32 (header.version);
380             break;
381 
382         default:
383             orc = RC (rcFS, rcFile, rcConstructing, rcByteOrder, rcInvalid);
384             PLOGERR (klogErr, (klogErr, rc, "invalid byte order flag '$(F); in "
385                                "encrypted file header", "F=%X",
386                                header.byte_order));
387             if (rc == 0)
388                 rc = orc;
389             break;
390         }
391 
392         if ((self->version > eCurrentVersion) || (self->version == 0))
393         {
394             orc = RC (rcFS, rcFile, rcConstructing, rcHeader, rcBadVersion);
395             PLOGERR (klogErr, (klogErr, orc, "can not decrypt version '$(V)'",
396                                "V=%u", header.version));
397             if (rc == 0)
398                 rc = orc;
399         }
400         if (rc == 0)
401             self->has_header = true;
402     }
403     return rc;
404 }
405 
406 
407 /* -----
408  * HeaderWrite
409  */
410 #ifndef SENC_IS_NENC_FOR_WRITER
411 #define SENC_IS_NENC_FOR_WRITER 1
412 #endif
413 
414 static
KEncFileHeaderWrite(KEncFile * self)415 rc_t KEncFileHeaderWrite (KEncFile * self)
416 {
417     rc_t rc;
418     size_t num_writ;
419     const KEncFileHeader * head;
420 
421 #if SENC_IS_NENC_FOR_WRITER
422     head = self->sra
423         ? (self->bswap ? &const_bswap_header_sra : &const_header_sra)
424         : (self->bswap ? &const_bswap_header : &const_header);
425 #else
426     head = self->bswap ? &const_bswap_header : &const_header;
427 #endif
428 
429     rc = KEncFileBufferWrite (self, 0, head, sizeof * head, &num_writ);
430     if (rc)
431         LOGERR (klogErr, rc, "Failed to write encrypted file header");
432 
433     else
434         /* forced upgrade by writing the header */
435         self->version = eCurrentVersion;
436 
437     if (rc == 0)
438         self->has_header = true;
439 
440     return rc;
441 }
442 
KEncFileWriteHeader_v2(KFile * self)443 LIB_EXPORT rc_t CC KEncFileWriteHeader_v2  (KFile * self)
444 {
445     if (self == NULL)
446         return RC (rcKrypto, rcFile, rcWriting, rcSelf, rcNull);
447 
448     return KEncFileHeaderWrite ((KEncFile*)self);
449 }
450 
451 
452 
453 /* ----------------------------------------------------------------------
454  * operations on KEncFileFooter
455  */
456 
457 /* ----------
458  * Validate
459  * we expect to read a Footer when we expect a Block so we validate in RAM
460  * without a Read
461  *
462  * we're just comparing the values in the footer against expected values
463  */
464 static
KEncFileFooterValidate(const KEncFile * self,const uint64_t block_count,const uint64_t crc_checksum)465 rc_t KEncFileFooterValidate (const KEncFile * self,
466                              const uint64_t block_count,
467                              const uint64_t crc_checksum)
468 {
469     rc_t rc1 = 0, rc2 = 0;
470 
471     assert (self);
472 
473     if (self->foot.block_count != block_count)
474     {
475         rc1 = RC (rcKrypto, rcFile, rcValidating, rcFile, rcCorrupt);
476         LOGERR (klogErr, rc1, "bad block count in encrypted file footer");
477     }
478     /*
479      * crcs should match or the crc should be 0 and the version is 1
480      */
481     if ((self->foot.crc_checksum != crc_checksum) &&
482         ((self->version == 1) || (crc_checksum != 0)))
483     {
484         rc2 = RC (rcKrypto, rcFile, rcValidating, rcChecksum, rcCorrupt);
485         LOGERR (klogErr, rc2, "bad crc checksum in encrypted file footer");
486     }
487     return (rc1 ? rc1 : rc2);
488 }
489 
490 
491 static __inline__
KEncFileFooterSwap(const KEncFile * self,KEncFileFooter * foot)492 void KEncFileFooterSwap (const KEncFile * self, KEncFileFooter * foot)
493 {
494     assert (self);
495     assert (foot);
496 
497     if (self->bswap)
498     {
499         assert (sizeof (foot->block_count) == 8);
500         foot->block_count = bswap_64 (foot->block_count);
501 
502         assert (sizeof (foot->crc_checksum) == 8);
503         foot->crc_checksum = bswap_64 (foot->crc_checksum);
504     }
505 }
506 
507 
508 /* ----------
509  * Read
510  * If we know where the footer is we can read it specifically
511  */
512 #if 0
513 static
514 rc_t KEncFileFooterRead (KEncFile * self, const uint64_t pos,
515                          const bool validate)
516 {
517     union foot_plus
518     {
519         KEncFileFooter foot;
520         uint8_t bytes [sizeof (KEncFileFooter) + 1];
521     } u;
522     size_t num_read;
523     rc_t rc;
524 
525     assert ((self->size_known == true) || (self->size_known == false));
526     assert (((self->size_known == true) &&
527              ((pos + sizeof (u.foot)) == self->enc_size)) ||
528             (pos == self->enc_size));
529 
530     /* try to read too much just to validate nothing after the footer */
531     rc = KEncFileBufferRead (self, pos, u.bytes, sizeof u.bytes, &num_read);
532     if (rc)
533         PLOGERR (klogErr, (klogErr, rc, "failed to read encrypted file footer "
534                            "at '$(P)'", "P=%lu", pos));
535 
536     else if (num_read == sizeof u.foot)
537     {
538         KEncFileFooterSwap (self, &u.foot);
539 
540         if (validate)
541             rc = KEncFileFooterValidate (self, u.foot.block_count,
542                                          u.foot.crc_checksum);
543 
544         if (rc == 0)
545         {
546             self->foot.block_count = u.foot.block_count;
547             self->foot.crc_checksum = u.foot.crc_checksum;
548         }
549     }
550     else if (num_read < sizeof u.foot)
551         rc = RC (rcKrypto, rcFile, rcReading, rcSize, rcInsufficient);
552 
553     else
554     {
555         assert (num_read > sizeof u.foot);
556         rc = RC (rcKrypto, rcFile, rcReading, rcSize, rcExcessive);
557     }
558 
559     return rc;
560 }
561 #endif
562 
563 /* ----------
564  * Write
565  * when we write a footer we write from the values in the object.  They
566  * are stored in the same object format as the footer so its very simple
567  */
568 static
KEncFileFooterWrite(KEncFile * self)569 rc_t KEncFileFooterWrite (KEncFile * self)
570 {
571     KEncFileFooter foot;
572     uint64_t pos;
573     size_t num_writ;
574     rc_t rc;
575 
576     if (self->sought)
577     {
578         self -> foot . block_count = foot . block_count =
579             PlaintextSize_to_BlockCount ( self -> dec_size, NULL );
580         foot . crc_checksum = 0;
581     }
582     else
583     {
584         memmove ( & foot, & self -> foot, sizeof foot );
585     }
586 
587     KEncFileFooterSwap (self, &foot);
588 
589     if (self->sought)
590         foot.crc_checksum = 0;
591 
592     pos = BlockId_to_CiphertextOffset ( self -> foot . block_count );
593 
594     assert ((self->size_known == true) || (self->size_known == false));
595 
596     rc = KEncFileBufferWrite (self, pos, &foot, sizeof (foot),
597                               &num_writ);
598     if (rc == 0)
599     {
600         if (num_writ != sizeof (foot))
601         {
602             rc = RC (rcFS, rcFile, rcWriting, rcFile, rcInsufficient);
603             LOGERR (klogErr, rc, "faled to write correctly sized fotter "
604                     "for encrypted file");
605         }
606         else
607             assert ((pos + sizeof (foot)) == self->enc_size);
608     }
609     return rc;
610 }
611 
612 
613 /* ----------
614  * IvecInit
615  *    create the ivec for a given block
616  *    done in a function to ensure decrypt and encrypt use the same code to
617  *    generate this.  Anything used to create this has to be available to
618  *    code that doesn't know the content of the data or the state of the file
619  *    beyond the location of the block with in the file.
620  *
621  *    This is definitely over-kill using the MD5.
622  */
623 static __inline__
KEncFileIVecInit(const uint64_t block_id,KEncFileIVec * ivec)624 void KEncFileIVecInit (const uint64_t block_id, KEncFileIVec * ivec)
625 {
626     BufferCalcMD5 (&block_id, sizeof block_id, ivec->ivec);
627 }
628 
629 
630 /* ----------
631  * BlockEncrypt
632  *
633  * Not thread safe - use of cipher schedules ivec and block key in the ciphers
634  *
635  * If this function were a protected region where only one thread could be in
636  * the body of this function at a time it could be made thread safe.
637  */
638 static
KEncFileBlockEncrypt(KEncFile * self,KEncFileBlock * d,KEncFileBlock * e)639 rc_t KEncFileBlockEncrypt (KEncFile * self, KEncFileBlock * d,
640                            KEncFileBlock * e)
641 {
642     SHA256State state;
643     uint64_t id;
644     uint16_t valid;
645     uint16_t saved_valid;
646     KEncFileCRC crc;
647     KEncFileIVec ivec;
648     rc_t rc;
649 
650     assert (self);
651     assert (d);
652     assert (e);
653 
654     /*
655      * First we finish preparing the two ciphers by creating the block
656      * user key out of the first part of the data and the shared Initialization
657      * vector for Chained Block Cipher mode encryption out of the block id
658      *
659      * create the initialization vector for this block
660      */
661     KEncFileIVecInit (d->id, &ivec);
662 
663     /*
664      * set the ivec for both the master and data block ciphers
665      */
666     rc = KCipherSetEncryptIVec (self->ciphers.master, &ivec);
667     if (rc)
668         return rc;
669 
670     rc = KCipherSetEncryptIVec (self->ciphers.block, &ivec);
671     if (rc)
672         return rc;
673 
674     /*
675      * create the block user key out of the first 4kb of data and the block id
676      */
677     saved_valid = valid = d->u.valid;
678     id = d->id;
679 
680     SHA256StateInit (&state);
681     SHA256StateAppend (&state, d->data,
682                        valid > 4096 ? 4096 : valid);
683     SHA256StateAppend (&state, &id, sizeof (id));
684     SHA256StateFinish (&state, d->key);
685 
686     /*
687      * create the block key schedule out of the block user key
688      */
689     rc = KCipherSetEncryptKey (self->ciphers.block, d->key, sizeof d->key);
690     if (rc)
691         return rc;
692 
693     /*
694      * Salt the block using the randomish user key to randomly select
695      * data from the valid data.
696      *
697      * This will cover the data portion of the block past the last valid
698      * byte.
699      *
700      * NOTE we are accessing a byte array as a word array. COuld be trouble
701      *      on some archaic processors such as the MC68000 family.
702      *
703      * NOTE we are using the array named data to access data beyond it's end
704      *      based on knowledge of the structure of the KEncFileBlock.
705      */
706     {
707         uint16_t * pw;
708         unsigned int windex;
709         unsigned int rindex;
710         size_t bindex;
711 
712         pw = (uint16_t*)d->key;
713         windex = 0;
714 
715         for (bindex = valid;
716              bindex < sizeof d->data + sizeof d->u;
717              ++ bindex)
718         {
719             /* this goes beyond the end of the data array by design */
720             rindex = (size_t)pw[windex];
721             rindex %= bindex;
722 
723             d->data[bindex] = d->data[rindex];
724 
725             ++rindex;
726             if (rindex >= sizeof self->block.key / sizeof *pw)
727                 rindex = 0;
728         }
729     }
730 
731     /*
732      * If we are modifying a block created on a system with a different default
733      * Endian choice we'll need to byte swap the block id and the block valid
734      * count
735      */
736     if (self->bswap)
737     {
738         assert (sizeof id == 8);
739         id = bswap_64 (id);
740 
741         assert (sizeof (valid = 2));
742         valid = bswap_16 (valid);
743     }
744 
745     /* is this a bswap problem? */
746     if (saved_valid == sizeof d->data)
747         d->u.valid |= valid;
748     else
749         d->u.valid = valid;
750 
751     e->id = id;
752 
753     /*
754      * encrypt the block user key into the buffer
755      */
756 #if 0
757     {
758         unsigned iii;
759         KOutMsg ("v2 decrypted key %lu\n", d->id);
760         for (iii = 0; iii < sizeof (d->key); +++ iii)
761             KOutMsg ("%2.2x ", d->key[iii]);
762         KOutMsg ("\n");
763     }
764 #endif
765     rc = KCipherEncryptCBC (self->ciphers.master, d->key, e->key,
766                             sizeof (d->key) / sizeof (ivec));
767     if (rc)
768         return rc;
769 #if 0
770     {
771         unsigned iii;
772         KOutMsg ("v2 encrypted key\n");
773         for (iii = 0; iii < sizeof (e->key); +++ iii)
774             KOutMsg ("%2.2x ", e->key[iii]);
775         KOutMsg ("\n");
776     }
777 #endif
778     /*
779      * encrypt the data, offset and valid values
780      */
781     rc = KCipherEncryptCBC (self->ciphers.block,
782                             d->data, e->data,
783                             (sizeof d->data + sizeof d->u) / sizeof (ivec));
784     if (rc)
785         return rc;
786 
787     d->u.valid = saved_valid;
788 
789     crc = CRC32 (0, e, (char*)(&e->crc)-(char*)e);
790 
791     self->block.crc = crc;
792 
793     if (self->bswap)
794     {
795         assert (sizeof crc == 4);
796         crc = bswap_32 (crc);
797     }
798     e->crc_copy = e->crc = crc;
799 
800 /*     KOutMsg ("%s: %lu %lu %lu ", __func__, self->foot.block_count, self->foot.crc_checksum,self->block.id); */
801     if (self->foot.block_count <= self->block.id)
802         self->foot.block_count = self->block.id + 1;
803 
804     if (!self->sought)
805         self->foot.crc_checksum += crc;
806 
807 /*     KOutMsg ("%lu %lu\n", __func__, self->foot.block_count, self->foot.crc_checksum); */
808 
809     return 0;
810 }
811 
812 
813 /* ----------
814  * BlockDecrypt
815  *   decrypt decrypts the data from a KEncFileBlock into the KEncFileBlock
816  *   in the KEncFile object
817  *
818  * Not thread safe - use of cipher schedules ivec and block key in the ciphers
819  *
820  * If this function were a protected region where only one thread could be in
821  * the body of this function at a time it could be made thread safe.
822  */
823 static
KEncFileBlockDecrypt(KEncFile * self,KEncFileBlockId bid,const KEncFileBlock * e,KEncFileBlock * d)824 rc_t KEncFileBlockDecrypt (KEncFile * self, KEncFileBlockId bid,
825                            const KEncFileBlock * e, KEncFileBlock * d)
826 {
827     KEncFileIVec ivec;
828     rc_t rc;
829 
830     d->id = e->id;
831 
832     /* create the initialization vector for this block */
833     KEncFileIVecInit (bid, &ivec);
834 
835     /*
836      * set the ivec for both the master and data block ciphers
837      */
838     rc = KCipherSetDecryptIVec (self->ciphers.master, &ivec);
839     if (rc)
840         return rc;
841 
842     rc = KCipherSetDecryptIVec (self->ciphers.block, &ivec);
843     if (rc)
844         return rc;
845 
846     /*
847      * decrypt the block key and initial vector using the user key and
848      * the computer ivec
849      */
850     rc = KCipherDecryptCBC (self->ciphers.master, e->key, d->key,
851                             (sizeof e->key) / sizeof ivec);
852     if (rc)
853         return rc;
854 
855     /*
856      * now create the AES key for the block from the newly decrypted
857      * block key
858      */
859     rc = KCipherSetDecryptKey (self->ciphers.block, d->key,
860                                sizeof d->key);
861     if (rc)
862         return rc;
863 
864     rc = KCipherDecryptCBC (self->ciphers.block, e->data, d->data,
865                             (sizeof e->data + sizeof e->u) / sizeof ivec);
866     if (rc)
867         return rc;
868 
869     if (self->bswap)
870     {
871         assert (sizeof d->u.valid == 2);
872         d->u.valid = bswap_16 (d->u.valid);
873     }
874 
875     if (d->u.valid >= sizeof d->data)
876         d->u.valid = sizeof d->data;
877     else
878         memset (d->data + d->u.valid, 0, sizeof d->data - d->u.valid);
879 
880     return rc;
881 }
882 
883 
884 /*
885  * if not decrypting block can be NULL
886  */
887 
888 /*
889   TBD: figure out rational way to handle bad password making blocks look really weird
890   If we have one that looks like a partial but isn't the last block -what do we do?
891 */
892 
893 static
KEncFileBlockRead(KEncFile * self,KEncFileBlock * block,KEncFileBlockId block_id,bool validate)894 rc_t KEncFileBlockRead (KEncFile * self, KEncFileBlock * block,
895                         KEncFileBlockId block_id, bool validate)
896 {
897     union
898     {
899         KEncFileBlock b;
900         KEncFileFooter f;
901     } u;
902     size_t num_read;
903     uint64_t epos, dpos;
904     rc_t vrc, rc = 0;
905     bool missing;
906 
907     assert (self);
908     assert ((validate == false) || (validate == true));
909     /* we should be decrypting or validating - maybe both */
910     assert ((block != NULL) || (validate == true));
911 
912     /* translate block id into both encrypted and decrypted addresses */
913     epos = BlockId_to_CiphertextOffset ( block_id );
914     dpos = BlockId_to_PlaintextOffset ( block_id );
915 
916     missing = false;
917 
918     /* clear out target block */
919     if (block != NULL)
920         memset (block, 0, sizeof * block);
921 
922     rc = KEncFileBufferRead (self, epos, &u.b, sizeof u.b, &num_read);
923     if (rc)
924     {
925         PLOGERR (klogErr, (klogErr, rc, "Failure to read block '$(B)' at '$(E)'"
926                            " in encrypted file decrypted at '$(D)",
927                            "B=%lu,E=%lu,D=%lu", block_id, epos, dpos));
928     }
929     else
930     {
931         switch (num_read)
932         {
933         case 0:
934             self->eof = true;
935             /* ain't got no block here */
936             break;
937 
938         default:
939             /* Invalid size */
940             rc = RC (rcKrypto, rcFile, rcReading, rcBuffer, rcInsufficient);
941             PLOGERR (klogErr, (klogErr, rc, "Failure to read full block '$(B)' "
942                                "at '$(E)' in encrypted file decrypted at '$(D)",
943                                "B=%lu,E=%lu,D=%lu", block_id, epos, dpos));
944             break;
945 
946         case sizeof (u.f):
947             /* footer */
948             if (validate) /* validate before checking as missing */
949             {
950                 KEncFileFooterSwap (self, &u.f);
951 
952                 if (u.f.block_count != block_id)
953                 {
954                     vrc = RC (rcKrypto, rcFile, rcValidating, rcSize,
955                               rcIncorrect);
956                     PLOGERR (klogErr,  (klogErr, vrc, "read footer block count "
957                                         "'$(B)' does not match actual block "
958                                         "count '$(A)'", "B=%lu,A=%lu",
959                                         u.f.block_count, block_id));
960                     if ( rc == 0 )
961                         rc = vrc;
962                 }
963                 vrc = KEncFileFooterValidate (self, u.f.block_count,
964                                               u.f.crc_checksum);
965 
966 #if 1
967                 if ( rc == 0 )
968                     rc = vrc;
969 #else
970                 /* what is the significance of a NULL block? */
971                 if (block == NULL)
972                     rc = vrc;
973 #endif
974             }
975 
976             /* is it a "missing" footer? */
977             /* or if the footer appears invalid - make it "valid" */
978             if (((missing = BufferAllZero(&u.f, sizeof u.f)) == true) ||
979                 (self->foot.block_count != block_id))
980             {
981 /*                 self->foot.block_count = block_id; */
982                 self->foot.crc_checksum = 0;
983             }
984 
985             /* force some values though they might already be known */
986             self->enc_size = epos + sizeof u.f;
987             self->size_known = true;
988             self->dec_size = dpos;
989             self->eof = true;
990             break;
991 
992         case sizeof (u.b):
993             /* block */
994             self->eof = false;
995             /* is it a "missing" block? */
996             if ((missing = BufferAllZero(&u.b, sizeof u.b)) == true)
997             {
998                 if (validate)
999                 {
1000                     vrc = RC (rcKrypto, rcFile, rcValidating,
1001                                    rcData, rcNull);
1002 
1003                     PLOGERR (klogErr, (klogErr, vrc, "read missing block at "
1004                                        "block number '$(I)' encrypted position "
1005                                        "$(E) decrypted postion $(D)",
1006                                        "I=%lu,E=%lu,D=%lu", block_id, epos,
1007                                        dpos));
1008                     if ( rc == 0 )
1009                         rc = vrc;
1010                 }
1011                 u.b.id = block_id;
1012                 u.b.u.valid = sizeof u.b.data;
1013 
1014                 /* if we can only learn of the size by reading and are thus scanning
1015                  * through the current decrypt position must be the current known
1016                  * decrypted side size
1017                  */
1018                 if (!self->size_known)
1019                 {
1020                     assert (dpos == self->dec_size);
1021                     self->dec_size = dpos + sizeof u.b.data;
1022                 }
1023                 /*
1024                  * if we know the decrypted size and it is less than what we
1025                  * read, adjust the valid.  BUT this must not be for the block read
1026                  * for the last block to know the decrypted size. A chicken and egg
1027                  * problem.
1028                  */
1029                 else if ((self->dec_size >= dpos) &&
1030                          (self->dec_size < dpos + sizeof u.b.data))
1031                     u.b.u.valid  = (uint16_t)(self->dec_size - dpos);
1032             }
1033 
1034             /* we read a full block that wasn't all zeroes */
1035             else
1036             {
1037                 /* since we've chosen not to standardize the file format byte ordering */
1038                 if (self->bswap)
1039                 {
1040                     assert (sizeof u.b.crc == 4);
1041                     u.b.crc = bswap_32 (u.b.crc);
1042                     u.b.crc_copy = bswap_32 (u.b.crc_copy);
1043 
1044                     assert (sizeof u.b.id == 8);
1045                     u.b.id = bswap_64 (u.b.id);
1046                 }
1047 
1048                 if (validate)
1049                 {
1050                     uint32_t crc;
1051 
1052                     if (block_id != u.b.id)
1053                     {
1054                         vrc = RC (rcKrypto, rcFile, rcValidating, rcIndex,
1055                                   rcIncorrect);
1056                         PLOGERR (klogErr, (klogErr, vrc, "error validating id "
1057                                            "for block '$(BID)' is not $(C2)",
1058                                            "BID=%lu,C2=%lu", block_id, u.b.id));
1059                         if ( rc == 0 )
1060                             rc = vrc;
1061                     }
1062 
1063                     crc = CRC32 (0, &u.b, (char*)&u.b.crc - (char*)&u.b);
1064 
1065                     if (crc != u.b.crc)
1066                     {
1067                         vrc = RC (rcKrypto, rcFile, rcValidating, rcChecksum, rcCorrupt);
1068                         PLOGERR (klogErr,
1069                                  (klogErr,
1070                                   vrc,
1071                                   "error validating crc for block '$(BID)' $(C1) is not $(C2)",
1072                                   "BID=%lu,C1=0x%X,C2=0x%X", block_id,
1073                                   crc, u.b.crc));
1074                         if ( rc == 0 )
1075                             rc = vrc;
1076                     }
1077                     if (crc != u.b.crc_copy)
1078                     {
1079                         vrc = RC (rcKrypto, rcFile, rcValidating, rcChecksum, rcCorrupt);
1080                         PLOGERR (klogErr,
1081                                  (klogErr,
1082                                   vrc,
1083                                   "error validating crc_copy for block '$(BID)' $(C1) is not $(C2)",
1084                                   "BID=%lu,C1=0x%X,C2=0x%X", block_id,
1085                                   crc, u.b.crc_copy));
1086                         if ( rc == 0 )
1087                             rc = vrc;
1088                     }
1089                 }
1090             }
1091             if (self->sought == false)
1092             {
1093                 if (block_id == 0)
1094                 {
1095                     self->foot.block_count = 1;
1096                     self->foot.crc_checksum = u.b.crc;
1097                 }
1098                 else
1099                 {
1100                     ++self->foot.block_count;
1101                     self->foot.crc_checksum += u.b.crc;
1102                 }
1103             }
1104 
1105             if (block != NULL)
1106             {
1107                 if (missing)
1108                 {
1109                     if (self->dad.write_enabled == false)
1110                         rc = RC (rcKrypto, rcFile, rcReading, rcData, rcIncomplete);
1111                     else
1112                     {
1113                         memmove (block, &u.b, sizeof u.b);
1114                         rc = 0;
1115                     }
1116                 }
1117                 else
1118                 {
1119                     rc = KEncFileBlockDecrypt (self, block_id, &u.b, block);
1120                     if (rc == 0)
1121                     {
1122                         if (block_id == 0)
1123                         {
1124                             rc_t sra = KFileIsSRA ((const char *)block->data, block->u.valid);
1125                             self->sra =  (sra == 0);
1126                         }
1127 
1128                         if (!self->size_known)
1129                         {
1130                             assert (dpos == self->dec_size);
1131                             self->dec_size = dpos + sizeof u.b.u.valid;
1132                             if (u.b.u.valid != sizeof u.b.data)
1133                             {
1134                                 self->size_known = true;
1135                                 self->enc_size = epos + sizeof u.b + sizeof self->foot;
1136                             }
1137                         }
1138                     }
1139                 }
1140             }
1141             break;
1142         }
1143     }
1144     return rc;
1145 }
1146 
1147 
1148 /*
1149  * Take a dirty block, encrypt it and write it to the backing file
1150  */
1151 static
KEncFileBlockFlush(KEncFile * self,KEncFileBlock * dec_block)1152 rc_t KEncFileBlockFlush (KEncFile * self, KEncFileBlock * dec_block)
1153 {
1154     rc_t rc = 0;
1155 
1156     assert (self);
1157     assert (dec_block);
1158 
1159 
1160     if (dec_block->id == 0)
1161     {
1162         rc = KFileIsSRA ((const char *)(dec_block->data), sizeof (KSraHeader));
1163 
1164         /* we wait ALL the way until we try to flush the first block before we set
1165          * the sra flag for write only files.
1166          * we get it when we read the first block otherwise.
1167          */
1168         if (self->sra != (rc == 0))
1169         {
1170             self->sra = (rc == 0);
1171             self->has_header = false;
1172         }
1173     }
1174     if ((dec_block->id == 0) || (self->seekable))
1175     {
1176         if (!self->has_header)
1177         {
1178             if (!self->swarm)
1179             {
1180                 rc = KEncFileHeaderWrite (self);
1181                 if (rc)
1182                     return rc;
1183             }
1184             else if (dec_block->id == 0)
1185                 self->enc_size = sizeof (KEncFileHeader);
1186         }
1187     }
1188 
1189 /*     if (self->dirty) */
1190     {
1191         KEncFileBlock enc_block;
1192 
1193         rc = KEncFileBlockEncrypt (self, dec_block, &enc_block);
1194         if (rc == 0)
1195         {
1196             KEncFileBlockId block_id;
1197             uint64_t pos;
1198             size_t num_writ;
1199 
1200             block_id = dec_block->id;
1201 
1202             pos = BlockId_to_CiphertextOffset ( block_id );
1203 
1204             rc = KEncFileBufferWrite (self, pos, &enc_block, sizeof enc_block,
1205                                       &num_writ);
1206 
1207             if (rc)
1208                 PLOGERR (klogErr, (klogErr, rc,
1209                                    "error writing encrypted block '$(B)'",
1210                                    "B=%lu", block_id));
1211 
1212             else if (num_writ != sizeof enc_block)
1213             {
1214                 rc = RC (rcKrypto, rcFile, rcWriting, rcBuffer, rcInsufficient);
1215                 PLOGERR (klogErr, (klogErr, rc, "error writing encrypted block "
1216                                    "'$(B)' wrote '$(Z)' not '$(Y)'",
1217                                    "B=%lu, Z=%zu", block_id, num_writ,
1218                                    sizeof enc_block));
1219             }
1220             else
1221                 self->dirty = false;
1222         }
1223     }
1224 
1225     return rc;
1226 }
1227 
1228 
1229 /* ----------------------------------------------------------------------
1230  * Interface Functions
1231  *
1232  * Destroy
1233  *
1234  */
1235 static
KEncFileDestroy(KEncFile * self)1236 rc_t CC KEncFileDestroy (KEncFile *self)
1237 {
1238     rc_t rc1 = 0;
1239     rc_t rc2 = 0;
1240     rc_t rc3 = 0;
1241     rc_t rc4 = 0;
1242     rc_t rc5 = 0;
1243     rc_t rc6 = 0;
1244 
1245     assert (self);
1246 
1247     if (self->dad.write_enabled)
1248     {
1249         /*
1250          * write the header if we've written nothing to an empty file
1251          * or if we've written something which will mean a change to v2
1252          * of the encrypted file format
1253          */
1254         if ((self->dec_size == 0) || (self->seekable && self->changed) ||
1255             ((self->dec_size == 0) && (!self->dad.read_enabled) && self->changed && (self->has_header == false)))
1256 /* SMURF IX
1257             (self->has_header == false))
1258  */
1259             rc1 = KEncFileHeaderWrite (self);
1260 
1261         /* write any dirty block */
1262         if (self->dirty)
1263             rc2 = KEncFileBlockFlush (self, &self->block);
1264 
1265         /* [re]write footer */
1266         if (self->changed)
1267             rc3 = KEncFileFooterWrite (self);
1268     }
1269     rc4 = KFileRelease (self->encrypted);
1270     rc5 = KCipherRelease (self->ciphers.master);
1271     rc6 = KCipherRelease (self->ciphers.block);
1272 
1273     free (self);
1274 
1275     if (rc1)
1276         return rc1;
1277     if (rc2)
1278         return rc2;
1279     if (rc3)
1280         return rc3;
1281     if (rc4)
1282         return rc4;
1283     if (rc5)
1284         return rc5;
1285     return rc6;
1286 }
1287 
1288 
1289 /* ----------------------------------------------------------------------
1290  * GetSysFile
1291  *  returns an underlying system file object
1292  *  and starting offset to contiguous region
1293  *  suitable for memory mapping, or NULL if
1294  *  no such file is available.
1295  *
1296  * We do not allow this for read, write or update as you can not memory map the
1297  * unencrypted file in a meaningful way.
1298  */
1299 static
KEncFileGetSysFile(const KEncFile * self,uint64_t * offset)1300 struct KSysFile *CC KEncFileGetSysFile (const KEncFile *self, uint64_t *offset)
1301 {
1302     assert (self);
1303     assert (offset);
1304 
1305     return NULL;
1306 }
1307 
1308 
1309 /* ----------------------------------------------------------------------
1310  * RandomAccess
1311  *
1312  *  returns 0 if random access, error code otherwise
1313  */
1314 static
KEncFileRandomAccess(const KEncFile * self)1315 rc_t CC KEncFileRandomAccess (const KEncFile *self)
1316 {
1317     assert (self != NULL);
1318     assert ((self->seekable == true) || (self->seekable == false));
1319 
1320     /* we checked for random access in the contructor */
1321     if (self->seekable)
1322         return 0;
1323 
1324     return RC (rcFS, rcFile, rcUpdating, rcFunction, rcUnsupported);
1325 }
1326 
1327 
1328 /* ----------------------------------------------------------------------
1329  * Size
1330  *  returns size in bytes of file
1331  *
1332  *  "size" [ OUT ] - return parameter for file size
1333  */
1334 static
KEncFileSize(const KEncFile * self,uint64_t * size)1335 rc_t CC KEncFileSize (const KEncFile *self, uint64_t *size)
1336 {
1337     if (!self->size_known)
1338         return RC (rcKrypto, rcFile, rcAccessing, rcSize, rcUnsupported);
1339 
1340     *size = self->dec_size;
1341     return 0;
1342 }
1343 
1344 
1345 /* ----------------------------------------------------------------------
1346  * SetSize
1347  *  sets size in bytes of file
1348  *
1349  *  "size" [ IN ] - new file size
1350  *
1351  * This is the size of the decrypted payload not of the encrypted file
1352  */
1353 static
KEncFileSetSizeBlockFull(KEncFile * self,uint64_t block_id)1354 rc_t KEncFileSetSizeBlockFull (KEncFile *self, uint64_t block_id)
1355 {
1356     if ((self->block.id == block_id) && (self->block.u.valid != 0))
1357     {
1358         if (self->block.u.valid < sizeof self->block.data)
1359         {
1360             self->block.u.valid = sizeof self->block.data;
1361             self->dirty = true;
1362         }
1363         else
1364             assert (self->block.u.valid  == sizeof self->block.data);
1365     }
1366     else
1367     {
1368         KEncFileBlock block;
1369         rc_t rc;
1370 
1371         rc = KEncFileBlockRead (self, &block, block_id, false);
1372         if (rc)
1373             return rc;
1374 
1375 /*         if (self->block.u.valid  != sizeof self->block.data) */
1376 /*         { */
1377 /*             OUTMSG (("%s: %u %u\n", __func__,self->block.u.valid,sizeof self->block.data)); */
1378 /*             OUTMSG (("%s: %lu %lu\n", __func__,self->block.id,block_id)); */
1379 /*         } */
1380 
1381         /* only change block if not "missing" */
1382         if (BufferAllZero (&block, sizeof block) == false)
1383         {
1384             assert (block.id == block_id);
1385 
1386             /* only change if not already full - shouldnt get here if not? */
1387             if (block.u.valid < sizeof block.data)
1388             {
1389                 self->changed = self->sought = true;
1390                 block.u.valid = sizeof block.data;
1391 
1392                 rc = KEncFileBlockFlush (self, &block);
1393                 if (rc)
1394                     return rc;
1395             }
1396             else
1397                 assert (self->block.u.valid  == sizeof self->block.data);
1398         }
1399     }
1400     return 0;
1401 }
1402 
1403 
1404 static
KEncFileSetSizeBlockPartial(KEncFile * self,uint64_t block_id,uint32_t valid)1405 rc_t KEncFileSetSizeBlockPartial (KEncFile *self, uint64_t block_id, uint32_t valid)
1406 {
1407     if ((self->block.id == block_id) && (self->block.u.valid != 0))
1408     {
1409         if (self->block.u.valid != valid)
1410         {
1411             self->block.u.valid = valid;
1412             self->dirty = true;
1413         }
1414     }
1415     else
1416     {
1417         KEncFileBlock block;
1418         rc_t rc;
1419 
1420         rc = KEncFileBlockRead (self, &block, block_id, false);
1421         if (rc)
1422             return rc;
1423 
1424         /* only change block if not "missing" */
1425         if (BufferAllZero (&block, sizeof block) == false)
1426         {
1427             assert (block.id == block_id);
1428 
1429             if (block.u.valid != valid)
1430             {
1431                 self->changed = self->sought = true;
1432                 block.u.valid = valid;
1433 
1434                 rc = KEncFileBlockFlush (self, &block);
1435                 if (rc)
1436                     return rc;
1437             }
1438         }
1439     }
1440     return 0;
1441 }
1442 
1443 
1444 static
KEncFileSetSizeInt(KEncFile * self,uint64_t dec_size)1445 rc_t KEncFileSetSizeInt (KEncFile *self, uint64_t dec_size)
1446 {
1447     uint64_t trim_size = 0;
1448     uint64_t enc_size;
1449     bool do_size = true;
1450 
1451     rc_t rc = 0;
1452 
1453     /* should we not have been called? */
1454     if ((dec_size == self->dec_size) && (self->enc_size != 0))
1455         return 0;
1456 
1457     /* if wiping out the whole file */
1458     if (dec_size == 0)
1459     {
1460         trim_size = sizeof (KEncFileHeader);
1461         enc_size = (sizeof (KEncFileHeader) + sizeof (KEncFileFooter));
1462 
1463         /* if we did clear out the RAM structures to match */
1464         memset (&self->block, 0, sizeof self->block);
1465         memset (&self->foot, 0, sizeof self->foot);
1466         self->dirty = false;
1467         self->size_known = true;
1468         self->bswap = false;
1469         self->changed = true;
1470         self->sought = false;
1471         self->has_header = false;
1472         self->version = eCurrentVersion;
1473     }
1474     else
1475     {
1476         KEncFileBlockId new_bid;    /* block id of new last block */
1477         KEncFileBlockId new_fid;    /* block id of new footer / block count */
1478         uint32_t        new_doff;   /* bytes into last partial block */
1479 
1480         /*
1481          * determine sizes of decrypted virtual file
1482          * and encrypted 'real' file
1483          */
1484 
1485         /*
1486           NB - the following code utilizes a function for converting
1487           an OFFSET to a zero-based block-id. However, it passes in a size.
1488           By examining new_doff, it detects the case where "dec_size" is an
1489           exact multiple of plaintext block size, and takes the return to
1490           be a "block-id" of the footer, and new_bid to be the last block.
1491 
1492           in the case where "new_doff" is zero, "new_bid" will be the effective
1493           id of the footer, and "new_bid" will need to be adjusted to the previous
1494           full block.
1495 
1496           in the case where "new_doff" is not zero, "new_bid" will be the
1497           last data block id, and the footer will be one beyond.
1498 
1499           although the code utilizes incorrect and misleading primitives,
1500           it works.
1501          */
1502 
1503         new_fid = new_bid = PlaintextOffset_to_BlockId (dec_size, &new_doff);
1504         if (new_doff == 0)
1505             --new_bid; /* exactly fills a block */
1506         else
1507             ++new_fid;  /* leaves a partial block */
1508 
1509         enc_size = BlockId_to_CiphertextOffset ( new_fid ) + sizeof self->foot;
1510 
1511         /* are we starting with an empty file? It's easy if we are */
1512         if (self->dec_size == 0)
1513         {
1514             /* TBD - this looks incorrect... what about KEncFileHeader?
1515                the code below would use BlockId_to_CiphertextOffset()
1516              */
1517             trim_size = sizeof (KEncFileHeader);
1518 
1519             /* if we did clear out the RAM structures to match */
1520             memset (&self->block, 0, sizeof self->block);
1521             self->bswap = false;
1522             self->changed = true;
1523             self->version = eCurrentVersion;
1524         }
1525         else
1526         {
1527             KEncFileBlockId old_bid;    /* block id of old last block */
1528             KEncFileBlockId old_fid;    /* block id of old footer / block count */
1529             uint32_t        old_doff;   /* bytes into last partial block */
1530 
1531             old_fid = old_bid = PlaintextOffset_to_BlockId (self->dec_size, &old_doff);
1532             if (old_doff == 0)
1533                 --old_bid; /* exactly fills a block */
1534             else
1535                 ++old_fid;  /* leaves a partial block */
1536 
1537             /* are we only changing the last block? */
1538             if (old_bid == new_bid)
1539             {
1540                 assert ((self->dad.read_enabled == false) || (self->enc_size == enc_size));
1541 
1542                 if (new_doff == 0)
1543                 {
1544                     /* change from partial to full last block */
1545                     rc = KEncFileSetSizeBlockFull (self, new_bid);
1546                 }
1547                 else
1548                 {
1549                     /* resize last block */
1550                     rc = KEncFileSetSizeBlockPartial (self, new_bid, new_doff);
1551                 }
1552 
1553                 /* no need to resize underlying file */
1554                 do_size = false;
1555             }
1556             else
1557             {
1558 
1559                 /* truncating the file? */
1560                 if (dec_size < self->dec_size)
1561                 {
1562                     trim_size = BlockId_to_CiphertextOffset ( new_fid );
1563 
1564                     /* do we throw away the block in the object? */
1565                     if (self->block.id > new_bid)
1566                     {
1567                         self->dirty = false;
1568                         memset (&self->block, 0, sizeof self->block);
1569                     }
1570 
1571                     /* we only change the new last block if its now partial */
1572                     if ( new_doff != 0 )
1573                         rc = KEncFileSetSizeBlockPartial (self, new_bid, new_doff);
1574 
1575                 }
1576                 /* expanding the file */
1577                 else
1578                 {
1579                     assert (dec_size > self->dec_size);
1580 
1581                     trim_size = BlockId_to_CiphertextOffset ( old_fid );
1582 
1583                     /* make old last block a full block if it wasn't already */
1584                     if ( old_doff != 0 )
1585                         rc = KEncFileSetSizeBlockFull (self, old_bid);
1586                 }
1587             }
1588         }
1589     }
1590     if (rc == 0)
1591     {
1592         if (do_size)
1593         {
1594             /* first trim for some reason... sparse files? */
1595             rc = KFileSetSize (self->encrypted, trim_size);
1596             if (rc)
1597                 LOGERR (klogErr, rc, "failure to trim size of encrypted file");
1598             else
1599             {
1600                 /* now extend to encrypted size */
1601                 rc = KFileSetSize (self->encrypted, enc_size);
1602                 if (rc)
1603                     LOGERR (klogErr, rc, "failure to file size of encrypted file");
1604             }
1605         }
1606         if (rc == 0)
1607         {
1608             self->enc_size = enc_size;
1609             self->dec_size = dec_size;
1610         }
1611     }
1612     return rc;
1613 }
1614 
1615 
1616 static
KEncFileSetSize(KEncFile * self,uint64_t dec_size)1617 rc_t CC KEncFileSetSize (KEncFile *self, uint64_t dec_size)
1618 {
1619     assert (self);
1620     assert (self->encrypted);
1621 
1622     if (self->dad.write_enabled == false)
1623         return RC (rcKrypto, rcFile, rcResizing, rcFile, rcNoPerm);
1624 
1625     if ((self->seekable == false) || (self->size_known == false))
1626         return RC(rcKrypto, rcFile, rcAccessing, rcFunction, rcUnsupported);
1627 
1628     /* silently ignore changes in size that don't change the size */
1629     if (dec_size == self->dec_size)
1630         return 0;
1631 
1632     else
1633         return KEncFileSetSizeInt (self, dec_size);
1634 }
1635 
1636 
1637 /* ----------------------------------------------------------------------
1638  * Read
1639  *  read file from known position
1640  *
1641  *  "pos" [ IN ] - starting position within file
1642  *
1643  *  "buffer" [ OUT ] and "bsize" [ IN ] - return buffer for read
1644  *
1645  *  "num_read" [ OUT, NULL OKAY ] - optional return parameter
1646  *  giving number of bytes actually read
1647  */
1648 static
KEncFileRead(const KEncFile * cself,uint64_t pos,void * buffer,size_t bsize,size_t * num_read)1649 rc_t CC KEncFileRead (const KEncFile *cself,
1650                       uint64_t pos,
1651                       void *buffer,
1652                       size_t bsize,
1653                       size_t *num_read)
1654 {
1655     KEncFile * self = (KEncFile *)cself; /* mutable values */
1656     uint64_t max_bid;
1657     uint64_t block_id;
1658     uint32_t offset;
1659     rc_t rc = 0;
1660 
1661     assert (self); /* checked in file.c KFileRead() */
1662     /* pos can be 'anything' */
1663     assert (buffer); /* checked in file.c KFileRead() */
1664     assert (bsize); /* checked in file.c KFileRead() */
1665     assert (num_read); /* checked in file.c KFileRead() */
1666 
1667     assert (self->dad.read_enabled);
1668     assert (self->encrypted);
1669 
1670     /* do we have a decrypted_size? */
1671 
1672     block_id = PlaintextOffset_to_BlockId (pos, &offset);
1673 
1674     switch ( ( uint32_t ) self->size_known)
1675     {
1676     case 0:
1677 
1678         max_bid = EncryptedPos_to_BlockId (self->enc_size, NULL, NULL);
1679 
1680         /* if past end of file as we know it so far quick out */
1681         if (max_bid <= block_id)
1682             return 0;
1683         break;
1684 
1685     case 1:
1686         /* if past end of file quick out */
1687         if (pos > self->dec_size)
1688             return 0;
1689         break;
1690 
1691     default:
1692         assert (0 && self->size_known);
1693         break;
1694     }
1695 
1696     /*
1697      * are we on the wrong block?
1698      * Or are do we need to read the first block?
1699      */
1700     if ((block_id != self->block.id) || (self->block.u.valid == 0))
1701     {
1702         if ((!self->seekable) && (self->block.id + 1 != block_id))
1703         {
1704             rc = RC (rcFS, rcFile, rcReading, rcOffset, rcIncorrect);
1705             PLOGERR (klogErr, (klogErr, rc, "attempt to seek in encryption write at"
1706                                " '$(O)' seek to '$(P)'", "O=%lu,P=%lu",
1707                                BlockId_to_CiphertextOffset(self->block.id), pos));
1708         }
1709         else
1710         {
1711             /* flush any dirty block */
1712             if (self->dirty)
1713             {
1714                 assert (self->dad.write_enabled);
1715                 rc = KEncFileBlockFlush (self, &self->block);
1716             }
1717 
1718             /* now try to read in a new block */
1719             if (rc == 0)
1720                 rc = KEncFileBlockRead (self, &self->block, block_id, false);
1721 
1722             if (rc == 0)
1723             {
1724                 uint64_t read_max;
1725 
1726                 read_max = BlockId_to_PlaintextOffset ( block_id ) + self -> block . u . valid;
1727                 if (self->dec_size < read_max)
1728                     self->dec_size = read_max;
1729             }
1730         }
1731     }
1732 
1733     /*
1734      * if we are trying to read past the end of the file
1735      * return 0 with nothing read
1736      */
1737     if ((rc == 0) &&
1738         (block_id == self->block.id) &&
1739         (offset < self->block.u.valid))
1740     {
1741         size_t to_copy;
1742 
1743         to_copy = self->block.u.valid - offset;
1744         if (to_copy > bsize)
1745             to_copy = bsize;
1746 
1747         memmove (buffer, self->block.data + offset, to_copy);
1748         *num_read = to_copy;
1749     }
1750     return rc;
1751 }
1752 
1753 
1754 /* ----------------------------------------------------------------------
1755  * Write
1756  *  write file at known position
1757  *
1758  *  "pos" [ IN ] - starting position within file
1759  *
1760  *  "buffer" [ IN ] and "size" [ IN ] - data to be written
1761  *
1762  *  "num_writ" [ OUT, NULL OKAY ] - optional return parameter
1763  *  giving number of bytes actually written
1764  */
1765 static
KEncFileWrite(KEncFile * self,uint64_t pos,const void * buffer,size_t bsize,size_t * pnum_writ)1766 rc_t CC KEncFileWrite (KEncFile *self, uint64_t pos,
1767                        const void *buffer, size_t bsize,
1768                        size_t *pnum_writ)
1769 {
1770     rc_t rc = 0;
1771 
1772     assert (self);
1773     assert (buffer);
1774     assert (bsize > 0);
1775     assert (pnum_writ);
1776 
1777     assert (self->dad.write_enabled);
1778     assert (self->encrypted);
1779 
1780 /*     KOutMsg ("+++++\n%s: pos %lu\n",__func__,pos); */
1781 
1782     if (self->dec_size != pos)
1783     {
1784         /* write only does not allow seeks */
1785         if ((!self->dad.read_enabled) && (!self->swarm))
1786         {
1787             rc = RC (rcFS, rcFile, rcWriting, rcOffset, rcIncorrect);
1788             PLOGERR (klogErr, (klogErr, rc, "attempt to seek in encryption write at"
1789                                " '$(O)' seek to '$(P)'", "O=%lu,P=%lu",
1790                                BlockId_to_CiphertextOffset(self->block.id), pos));
1791         }
1792         else
1793             self->sought = true;
1794     }
1795     if (rc == 0)
1796     {
1797         uint64_t block_id;
1798         uint32_t offset;
1799         uint64_t block_max;
1800         uint64_t new_size;
1801 
1802         rc = 0;
1803         /* Block Id for this write */
1804         block_id = PlaintextOffset_to_BlockId (pos, &offset);
1805 
1806         block_max = BlockId_to_PlaintextOffset ( block_id + 1 );
1807 
1808         new_size = pos + bsize;
1809         if (new_size > block_max)
1810             bsize = block_max - new_size;
1811 
1812 
1813         /* is the new position beyond the current file length? */
1814         if ((new_size > self->dec_size) && (self->dad.read_enabled) && (!self->swarm))
1815         {
1816             rc = KEncFileSetSizeInt (self, new_size);
1817             if (rc)
1818                 return rc;
1819         }
1820 
1821         /* are we going to a new block? */
1822         if ((block_id != self->block.id) || (self->block.u.valid == 0))
1823         {
1824             /* do we have sometihng to flush first? */
1825             if (self->dirty)
1826             {
1827                 assert (self->dad.write_enabled);
1828                 rc = KEncFileBlockFlush (self, &self->block);
1829             }
1830 
1831             if (rc == 0)
1832             {
1833                 /* if we are going to over write the whole block */
1834                 if ((!self->dad.read_enabled) ||
1835                     ((offset == 0) && (bsize >= sizeof (self->block.data))))
1836                 {
1837                     memset (&self->block, 0, sizeof self->block);
1838                     self->block.id = block_id;
1839                 }
1840                 /* else try to fetch an existing block */
1841                 else
1842                     rc = KEncFileBlockRead (self, &self->block, block_id, false);
1843             }
1844         }
1845 
1846         if (rc == 0)
1847         {
1848             /* we are at the right block and ready to write */
1849             uint32_t new_valid;
1850             size_t to_copy;
1851 
1852             /*
1853              * force block id to be right even if fetch was beyond end of
1854              * existing file
1855              */
1856             self->block.id = block_id;
1857 
1858 /*             to_copy = sizeof self->block.data - self->block.u.valid; */
1859             to_copy = sizeof self->block.data - offset;
1860 
1861             if (to_copy > bsize)
1862                 to_copy = bsize;
1863 
1864             memmove (self->block.data + offset, buffer, to_copy);
1865             self->dirty = true;
1866             *pnum_writ = to_copy;
1867 
1868             new_valid = (uint32_t) ( offset + to_copy );
1869             if (new_valid > self->block.u.valid)
1870             {
1871                 uint64_t new_size;
1872 
1873                 self->block.u.valid = new_valid;
1874 
1875                 new_size = pos + to_copy;
1876                 if (new_size > self->dec_size)
1877                     self->dec_size = new_size;
1878             }
1879 
1880             if (self->swarm)
1881                 rc = KEncFileBlockFlush (self, &self->block);
1882 
1883 
1884         }
1885     }
1886     return rc;
1887 }
1888 
1889 
1890 /* ----------------------------------------------------------------------
1891  * Type
1892  *  returns a KFileDesc
1893  *  not intended to be a content type,
1894  *  but rather an implementation class
1895  *
1896  * Just return what the backing file says...
1897  */
1898 static
KEncFileType(const KEncFile * self)1899 uint32_t CC KEncFileType (const KEncFile *self)
1900 {
1901     assert (self != NULL);
1902     assert (self->encrypted != NULL);
1903 
1904     return KFileType (self->encrypted);
1905 }
1906 
1907 
1908 /* ----------------------------------------------------------------------
1909  * KEncFileMake
1910  *  create a new file object
1911  */
1912 
1913 /* ----------
1914  * KeysInit
1915  */
1916 static
KEncFileCiphersInit(KEncFile * self,const KKey * key,bool read,bool write)1917 rc_t KEncFileCiphersInit (KEncFile * self, const KKey * key, bool read, bool write)
1918 {
1919     KCipherManager * mgr;
1920     size_t z;
1921     rc_t rc;
1922 
1923     switch ( key->type)
1924     {
1925     default:
1926         return RC (rcKrypto, rcEncryptionKey, rcConstructing, rcParam, rcInvalid);
1927 
1928     case kkeyNone:
1929         return RC (rcKrypto, rcEncryptionKey, rcConstructing, rcParam, rcIncorrect);
1930 
1931     case kkeyAES128:
1932         z = 128/8; break;
1933 
1934     case kkeyAES192:
1935         z = 192/8; break;
1936 
1937     case kkeyAES256:
1938         z = 256/8; break;
1939     }
1940     rc = KCipherManagerMake (&mgr);
1941     if (rc == 0)
1942     {
1943         rc = KCipherManagerMakeCipher (mgr, &self->ciphers.master, kcipher_AES);
1944         if (rc == 0)
1945         {
1946             rc = KCipherManagerMakeCipher (mgr, &self->ciphers.block, kcipher_AES);
1947             if (rc == 0)
1948             {
1949                 rc = KCipherSetDecryptKey (self->ciphers.master, key->text, z);
1950                 if (rc == 0)
1951                 {
1952                     rc = KCipherSetEncryptKey (self->ciphers.master, key->text, z);
1953                     if (rc == 0)
1954                         goto keep_ciphers;
1955                 }
1956                 KCipherRelease (self->ciphers.block);
1957                 self->ciphers.block = NULL;
1958             }
1959             KCipherRelease (self->ciphers.master);
1960             self->ciphers.master = NULL;
1961         }
1962     keep_ciphers:
1963         KCipherManagerRelease (mgr);
1964     }
1965     return rc;
1966 }
1967 
1968 
1969 
1970 static const KFile_vt_v1 vtKEncFile =
1971 {
1972     /* version */
1973     1, 1,
1974 
1975     /* 1.0 */
1976     KEncFileDestroy,
1977     KEncFileGetSysFile,
1978     KEncFileRandomAccess,
1979     KEncFileSize,
1980     KEncFileSetSize,
1981     KEncFileRead,
1982     KEncFileWrite,
1983 
1984     /* 1.1 */
1985     KEncFileType
1986 };
1987 
1988 
1989 static
KEncFileMakeIntValidSize(uint64_t enc_size,bool w)1990 rc_t  KEncFileMakeIntValidSize (uint64_t enc_size, bool w)
1991 {
1992 
1993     if (enc_size == 0)
1994     {
1995         if (w)
1996             return 0;
1997     }
1998     else
1999     {
2000         uint64_t min_size = sizeof (KEncFileHeader) + sizeof (KEncFileFooter);
2001         uint64_t block_count;
2002 
2003         if (enc_size >= min_size)
2004         {
2005             block_count = EncryptedPos_to_BlockId (enc_size, NULL, NULL);
2006 
2007             if (enc_size - BlockId_to_CiphertextOffset (block_count) == sizeof (KEncFileFooter))
2008                 return 0;
2009         }
2010     }
2011     return RC (rcKrypto, rcFile, rcConstructing, rcSize, rcIncorrect);
2012 }
2013 
2014 
2015 /* ----------
2016  * MakeInt
2017  *    common make for all encryptor/decryptors
2018  */
2019 static
KEncFileMakeInt(KEncFile ** pself,KFile * encrypted,bool r,bool w,bool v,bool s)2020 rc_t KEncFileMakeInt (KEncFile ** pself, KFile * encrypted,
2021                       bool r, bool w, bool v, bool s)
2022 {
2023     uint64_t enc_size;
2024     rc_t rc = 0, orc;
2025     bool seekable;
2026     bool size_known;
2027 
2028     assert (pself);
2029     assert (encrypted);
2030     assert (((r == true) || (r == false)) &&
2031             ((w == true) || (w == false)) &&
2032             ((v == true) || (v == false)));
2033 
2034     /* must be able to do at elast one of read and write */
2035     assert (r || w);
2036 
2037     /* expecting to validate read only right now */
2038 /*     assert ((v && r && !w) || (!v)); */
2039 
2040     if (w && ! encrypted->write_enabled)
2041     {
2042         rc = RC (rcKrypto, rcFile, rcConstructing, rcFile, rcReadonly);
2043         LOGERR (klogErr, rc, "Can not make a encryptor for a unwritable file");
2044     }
2045     if (r && ! encrypted->read_enabled)
2046     {
2047         orc = RC (rcKrypto, rcFile, rcConstructing, rcFile, rcWriteonly);
2048         LOGERR (klogErr, orc, "Can not make a decryptor for an unreadable file");
2049         if (rc == 0)
2050             rc = orc;
2051     }
2052     if (rc)
2053         return rc;
2054 
2055     /* determine whether the original file can tell us the size */
2056     rc = KFileSize (encrypted, &enc_size);
2057     if (rc == 0)
2058         size_known = true;
2059 
2060     else if (GetRCState(rc) == rcUnsupported)
2061         size_known = false;
2062 
2063     else
2064         return rc;
2065 
2066     if (!v && size_known)
2067     {
2068         rc = KEncFileMakeIntValidSize (enc_size, w);
2069         if (rc)
2070             return rc;
2071     }
2072 
2073     /* determine whether the original file allows seeks */
2074     rc = KFileRandomAccess (encrypted);
2075     if (rc == 0)
2076         seekable = true;
2077 
2078     else if (GetRCState(rc) == rcUnsupported)
2079         seekable = false;
2080 
2081     else
2082     {
2083         LOGERR (klogErr, rc, "error checking random access building "
2084                 "encrypted file");
2085         return rc;
2086     }
2087 
2088     /* We are currently only supporting update on seekable and size_known original files */
2089     if (r && w && ((!seekable) || (!size_known)))
2090     {
2091         rc = RC (rcKrypto, rcFile, rcConstructing, rcFile, rcIncorrect);
2092         LOGERR (klogErr, rc, "encryptor/decryptor requires seek and size ability");
2093         return rc;
2094     }
2095 
2096     rc = KFileAddRef (encrypted);
2097     if (rc)
2098     {
2099         LOGERR (klogErr, rc, "Could not add reference to encrypted file");
2100         return rc;
2101     }
2102     else
2103     {
2104         KEncFile * self;
2105 
2106         /* allocate and zero out an object since we want much of it to be zeroed */
2107         self = calloc (1, sizeof *self);
2108         if (self == NULL)
2109         {
2110             rc = RC (rcFS, rcFile, rcConstructing, rcMemory, rcExhausted);
2111             LOGERR (klogSys, rc,
2112                     "out of memory creating encryptor and/or decryptor");
2113         }
2114         else
2115         {
2116             /* all KFiles get this initialization */
2117             rc = KFileInit (&self->dad, (const KFile_vt*)&vtKEncFile, "KEncFile", "no-name", r, w);
2118             if (rc)
2119                 LOGERR (klogInt, rc, "error with init for encrypted file");
2120 
2121             else
2122             {
2123                 self->encrypted = encrypted;
2124                 self->swarm = s;
2125 
2126                 /* write only or empty updatable */
2127                 if ((!r) || (w && size_known && (enc_size == 0)))
2128                 {
2129                     /* dummy size to make the SetSizeInt work */
2130                     self->enc_size = enc_size;
2131                     rc = KEncFileSetSizeInt (self, 0);
2132                     self->seekable = r && seekable;
2133                 }
2134                 else
2135                 {
2136                     self->enc_size = enc_size;
2137                     self->seekable = seekable;
2138                     self->size_known = size_known;
2139                     rc = KEncFileHeaderRead (self);
2140                 }
2141                 if (rc == 0)
2142                 {
2143                     *pself = self;
2144                     return 0;
2145                 }
2146             }
2147             free (self);
2148         }
2149         KFileRelease (encrypted);
2150     }
2151     return rc;
2152 }
2153 
2154 
2155 static
KEncFileMakeSize(KEncFile * self)2156 rc_t KEncFileMakeSize (KEncFile *self)
2157 {
2158     KEncFileBlockId fid;
2159     rc_t rc;
2160 
2161     assert (self->seekable);
2162 
2163     /*
2164      * turn the encrypted size into a block/offset
2165      * the offset should be 0 for a missing footer
2166      * or the size of a footer
2167      */
2168     fid = EncryptedPos_to_BlockId (self->enc_size, NULL, NULL);
2169 
2170     assert (BlockId_to_CiphertextOffset(fid) + sizeof (self->foot) == self->enc_size);
2171 
2172     if (fid == 0)
2173         self->dec_size = 0;
2174 
2175     else
2176     {
2177         KEncFileBlockId bid = fid - 1;
2178         KEncFileBlock b;
2179 
2180         /*
2181          * not calling this a seek as its not reading a data block
2182          * out of order that will be modified
2183          */
2184         rc = KEncFileBlockRead (self, &b, bid, false);
2185         if (rc)
2186             return rc;
2187         else
2188         {
2189             if (BufferAllZero(&b, sizeof b) == true)
2190                 self->dec_size = BlockId_to_PlaintextOffset (bid) +
2191                     sizeof self->block.data;
2192 
2193             else
2194                 self->dec_size = BlockId_to_PlaintextOffset (bid) + b.u.valid;
2195         }
2196     }
2197     self->size_known = true;
2198     return 0;
2199 }
2200 
2201 
2202 /* ----------
2203  * MakeCmn
2204  * common parameter validation for all encryptor/decryptors
2205  */
2206 static
KEncFileMakeCmn(KEncFile ** pself,KFile * encrypted,const KKey * key,bool r,bool w,bool s)2207 rc_t KEncFileMakeCmn (KEncFile ** pself, KFile * encrypted, const KKey * key,
2208                       bool r, bool w, bool s)
2209 {
2210     rc_t rc = 0, orc;
2211 
2212     assert (((r == true)||(r == false))&&((w == true)||(w == false)));
2213     assert (w || r);
2214 
2215     if (pself == NULL)
2216     {
2217         rc = RC (rcKrypto, rcFile, rcConstructing, rcSelf, rcNull);
2218         LOGERR (klogErr, rc,
2219                 "pointer to self NULL when creating "
2220                 "an encryptor/decryptor");
2221     }
2222     else
2223         *pself = NULL;
2224 
2225     if (encrypted == NULL)
2226     {
2227         orc = RC (rcFS, rcFile, rcConstructing, rcParam, rcNull);
2228         LOGERR (klogErr, orc,
2229                 "encrypted file not readable when creating "
2230                 "an encryptor/decryptor");
2231         if (rc == 0)
2232             rc = orc;
2233     }
2234 
2235     if (key == NULL)
2236     {
2237         orc = RC (rcFS, rcFile, rcConstructing, rcParam, rcNull);
2238         LOGERR (klogErr, orc,
2239                 "key not supplied when creating an encryptor/decryptor");
2240         if (rc == 0)
2241             rc = orc;
2242     }
2243 
2244     CRC32Init();    /* likely to be called way too often */
2245 
2246     switch (key->type)
2247     {
2248     default:
2249         orc = RC (rcFS, rcFile, rcConstructing, rcParam, rcInvalid);
2250         PLOGERR (klogErr,
2251                  (klogErr, orc, "invalid key type '$(T)' should be "
2252                   "kkeyAES128(1), kkeyAES192(2) or kkeyAES256(3)",
2253                   "T=%u", key->type));
2254         if (rc == 0)
2255             rc = orc;
2256         break;
2257 
2258     case kkeyAES128:
2259     case kkeyAES192:
2260     case kkeyAES256:
2261         break;
2262     }
2263     if (rc == 0)
2264     {
2265         KEncFile * self;
2266 
2267         assert ((r == true) || (r == false));
2268         assert ((w == true) || (w == false));
2269 
2270         rc = KEncFileMakeInt (&self, encrypted, r, w, false, s);
2271         if (rc == 0)
2272         {
2273             rc = KEncFileCiphersInit (self, key, r, w);
2274             if (rc == 0)
2275             {
2276 
2277                 if (self->seekable && self->size_known)
2278                     rc = KEncFileMakeSize (self);
2279 
2280                 if (rc == 0)
2281                 {
2282                     *pself = self;
2283                     return 0;
2284                 }
2285             }
2286             KFileRelease (&self->dad);
2287         }
2288     }
2289     return rc;
2290 }
2291 
2292 
2293 /* ----------
2294  * Read mode is fully seekable if the underlying KFile is seekable some
2295  * integrity checking will not be performed in allowing this seeking.
2296  */
KEncFileMakeRead_v2(const KFile ** pself,const KFile * encrypted,const KKey * key)2297 LIB_EXPORT rc_t CC KEncFileMakeRead_v2 (const KFile ** pself,
2298                                         const KFile * encrypted,
2299                                         const KKey * key)
2300 {
2301     KEncFile * self;
2302     rc_t rc;
2303 
2304     /*
2305      * casting encrypted dowsn't actually make it writable
2306      * it just lets us use a common constructor
2307      */
2308     rc = KEncFileMakeCmn (&self, (KFile *)encrypted, key, true, false, false);
2309     if (rc)
2310         LOGERR (klogErr, rc, "error constructing decryptor");
2311 
2312     else
2313         *pself = &self->dad;
2314 
2315     return rc;
2316 }
2317 
2318 
2319 /* ----------
2320  * Write mode encrypted file can only be written straight through form the
2321  * first byte to the last.
2322  *
2323  * Existing content is lost.
2324  */
KEncFileMakeWrite_v2(KFile ** pself,KFile * encrypted,const KKey * key)2325 LIB_EXPORT rc_t CC KEncFileMakeWrite_v2 (KFile ** pself,
2326                                          KFile * encrypted,
2327                                          const KKey * key)
2328 {
2329     KEncFile * self;
2330     rc_t rc;
2331 
2332     rc = KEncFileMakeCmn (&self, encrypted, key, false, true, false);
2333     if (rc)
2334         LOGERR (klogErr, rc, "error constructing encryptor");
2335 
2336     else
2337         *pself = &self->dad;
2338 
2339     return rc;
2340 }
2341 
2342 
KEncFileMakeUpdate_v2(KFile ** pself,KFile * encrypted,const KKey * key)2343 LIB_EXPORT rc_t CC KEncFileMakeUpdate_v2 (KFile ** pself,
2344                                           KFile * encrypted,
2345                                           const KKey * key)
2346 {
2347     KEncFile * self;
2348     rc_t rc;
2349 
2350 /*     static int count = 0; */
2351 
2352 /*     KOutMsg ("%s: %d\n",__func__,++count); */
2353 
2354     rc = KEncFileMakeCmn (&self, (KFile *)encrypted, key, true, true, false);
2355     if (rc)
2356         LOGERR (klogErr, rc, "error constructing encryptor/decryptor");
2357 
2358     else
2359         *pself = &self->dad;
2360 
2361     return rc;
2362 }
2363 
2364 
2365 /* ----------
2366  * Swarm mode encrypted file can be writtenout of order but the footer is not
2367  * handled automatically
2368  */
KEncFileMakeBlock_v2(KFile ** pself,KFile * encrypted,const KKey * key)2369 LIB_EXPORT rc_t CC KEncFileMakeBlock_v2 (KFile ** pself,
2370                                          KFile * encrypted,
2371                                          const KKey * key)
2372 {
2373     KEncFile * self;
2374     rc_t rc;
2375 
2376 /*     static int count = 0; */
2377 
2378 /*     KOutMsg ("%s: %d\n",__func__,++count); */
2379 
2380     rc = KEncFileMakeCmn (&self, (KFile *)encrypted, key, false, true, true);
2381     if (rc)
2382         LOGERR (klogErr, rc, "error constructing encryptor/decryptor");
2383 
2384     else
2385         *pself = &self->dad;
2386 
2387     return rc;
2388 }
2389 
2390 
2391 /* ----------
2392  * Validate mode is useful only for the KFileEncValidate function
2393  */
2394 static
KEncFileMakeValidate(KEncFile ** pself,const KFile * encrypted)2395 rc_t KEncFileMakeValidate (KEncFile ** pself, const KFile * encrypted)
2396 {
2397     KEncFile * self;
2398     rc_t rc;
2399 
2400     assert (pself);
2401     assert (encrypted);
2402 
2403     rc = KEncFileMakeInt (&self, (KFile*)encrypted, true, false, true, false);
2404     if (rc)
2405         LOGERR (klogErr, rc, "error making KEncFile");
2406     else
2407     {
2408         rc = KEncFileHeaderRead (self);
2409         if (rc)
2410             LOGERR (klogErr, rc, "error reading encrypted file header");
2411         else
2412         {
2413             *pself = self;
2414             return 0;
2415         }
2416     }
2417     *pself = NULL;
2418     return rc;
2419 }
2420 
2421 
2422 /* ======================================================================
2423  * Interface extensions
2424  */
2425 
2426 
2427 /* ----------
2428  * Validate mode can not be read or written.
2429  * Upon open the whole file is read from begining to end and all CRC
2430  * and other integrity checks are performed immedaitely
2431  *
2432  * This will fail if the file being tested is not "at position 0" and can not
2433  * be sought back to 0.
2434  */
2435 
2436 
2437 
KEncFileValidate_v2(const KFile * encrypted)2438 LIB_EXPORT rc_t CC KEncFileValidate_v2 (const KFile * encrypted)
2439 {
2440     KEncFile * file;
2441     rc_t rc = 0;
2442 
2443     /* fail if a NULL parameter: can't validate all addresses */
2444     if (encrypted == NULL)
2445     {
2446         rc = RC (rcKrypto, rcFile, rcValidating, rcParam, rcNull);
2447         LOGERR (klogErr, rc, "encrypted file was null when trying to validate");
2448         return rc;
2449     }
2450 
2451     /* file header is validated within the call to Make Validate */
2452     rc = KEncFileMakeValidate (&file, encrypted);
2453     if (rc)
2454         LOGERR (klogErr, rc,
2455                 "unable to validate encrypted file due to "
2456                 "inability to open as encrypted file");
2457     else
2458     {
2459         uint64_t pos;             /* position within the encrypted file */
2460         uint64_t block_count = 0; /* how many blocks have we read */
2461 
2462         /* loop through all data blocks */
2463         pos = sizeof (KEncFileHeader);
2464         for (block_count = 0; ; ++block_count)
2465         {
2466             rc_t vrc;
2467             STSMSG (2, ("reading block '%u' at '%lu'", block_count,
2468                         BlockId_to_CiphertextOffset(block_count)));
2469 
2470             vrc = KEncFileBlockRead (file, NULL, block_count, true);
2471             if (vrc != 0)
2472             {
2473                 if ( rc == 0 )
2474                     rc = vrc;
2475                 if ( GetRCContext( vrc ) != rcValidating )
2476                 {
2477                     STSMSG (2, ("read error at block '%u'", block_count));
2478                     break;
2479                 }
2480             }
2481             if (file->eof)
2482             {
2483                 STSMSG (2, ("block '%u' was end", block_count));
2484                 break;
2485             }
2486             pos += sizeof (KEncFileData);
2487         }
2488         KFileRelease (&file->dad);
2489     }
2490     return (rc);
2491 }
2492 
2493 
KEncFileHeaderWrite_v2(KFile * dad)2494 LIB_EXPORT rc_t CC KEncFileHeaderWrite_v2 (KFile * dad)
2495 {
2496     rc_t rc;
2497 
2498     if (dad->vt != (const KFile_vt*)&vtKEncFile)
2499     {
2500         rc = RC (rcKrypto, rcFile, rcWriting, rcType, rcIncorrect);
2501         LOGERR (klogErr, rc, "file not an encryptor requested writing header");
2502     }
2503     else
2504     {
2505         KEncFile * self;
2506         self = (KEncFile*)dad;
2507 
2508         rc = KEncFileHeaderWrite (self);
2509     }
2510     return rc;
2511 }
2512 
KEncFileFooterWrite_v2(KFile * dad)2513 LIB_EXPORT rc_t CC KEncFileFooterWrite_v2 (KFile * dad)
2514 {
2515     rc_t rc;
2516 
2517     if (dad->vt != (const KFile_vt*)&vtKEncFile)
2518     {
2519         rc = RC (rcKrypto, rcFile, rcWriting, rcType, rcIncorrect);
2520         LOGERR (klogErr, rc, "file not an encryptor requested writing footer");
2521     }
2522     else
2523     {
2524         KEncFile * self;
2525         self = (KEncFile*)dad;
2526 
2527         rc = KEncFileFooterWrite (self);
2528     }
2529     return rc;
2530 }
2531 
2532 /* ----------
2533  * Identify whether a file is a KEncFile type encrypted file by the header.
2534  * read the header into a buffer and pass it into this function.
2535  * The buffer_size needs to be at least 8 but more bytes lead to a better
2536  * check up to the size of the header of a KEncFile type encrypted file.
2537  * As the header may change in the future (in a backwards compatible way)
2538  * that size might change from the current 16.
2539  *
2540  * Possible returns:
2541  * 0:
2542  *      the file is an identified KEncFile type file.  False positives are
2543  *      possible if a file happens to match at 8 or more bytes
2544  *
2545  * RC (rcFS, rcFile, rcIdentifying, rcFile, rcWrongType)
2546  *      the file is definitely not a KEncFile type encrypted file.
2547  *
2548  * RC (rcFS, rcFile, rcIdentifying, rcParam, rcNull)
2549  *      bad parameters in the call
2550  *
2551  * RC (rcFS, rcFile, rcIdentifying, rcBuffer, rcInsufficient)
2552  *      not a large enough buffer to make an identification
2553  */
KFileIsEnc_v2(const char * buffer,size_t buffer_size)2554 LIB_EXPORT rc_t CC KFileIsEnc_v2 (const char * buffer, size_t buffer_size)
2555 {
2556     KEncFileHeader header;
2557     size_t count;
2558     bool byte_swapped;
2559 
2560     if ((buffer == NULL) || (buffer_size == 0))
2561         return RC  (rcFS, rcFile, rcIdentifying, rcParam, rcNull);
2562 
2563     /* must have the signature to consider it an Encrypted file */
2564     if (buffer_size < sizeof (header.file_sig))
2565         return RC (rcFS, rcFile, rcIdentifying, rcBuffer, rcInsufficient);
2566 
2567     if ((memcmp (buffer, &const_header.file_sig, sizeof const_header.file_sig ) != 0) &&
2568         (memcmp (buffer, &const_header_sra.file_sig, sizeof const_header_sra.file_sig ) != 0))
2569         return SILENT_RC (rcFS, rcFile, rcIdentifying, rcFile, rcWrongType);
2570 
2571     /* can we also check the byte order? It's okay if we can't */
2572     if (buffer_size < sizeof header.file_sig + sizeof header.byte_order)
2573         return 0;
2574 
2575     count = buffer_size > sizeof header ? sizeof header : buffer_size;
2576 
2577     memmove (&header, buffer, count);
2578 
2579     if (header.byte_order == const_header.byte_order)
2580         byte_swapped = false;
2581 
2582     else if (header.byte_order == const_bswap_header.byte_order)
2583         byte_swapped = true;
2584 
2585     /* but if it's not we fail with a different error */
2586     else
2587         return RC (rcFS, rcFile, rcIdentifying, rcFile, rcOutoforder);
2588 
2589     /* can we check the version as well? It's okay if we can't */
2590     if (buffer_size < sizeof (header))
2591         return 0;
2592 
2593     assert (sizeof (header.version) == 4);
2594     if (byte_swapped)
2595         header.version = bswap_32(header.version);
2596 
2597     /* and it's a different error if the version is not within our range */
2598     if ((header.version <= 0) || (header.version > eCurrentVersion))
2599         return RC (rcKrypto, rcFile, rcClassifying, rcFile, rcBadVersion);
2600 
2601     return 0;
2602 }
2603 
2604 
KFileIsSraEnc(const char * buffer,size_t buffer_size)2605 LIB_EXPORT rc_t CC KFileIsSraEnc (const char * buffer, size_t buffer_size)
2606 {
2607     KEncFileHeader header;
2608     size_t count;
2609     bool byte_swapped;
2610 
2611     if ((buffer == NULL) || (buffer_size == 0))
2612         return RC  (rcFS, rcFile, rcIdentifying, rcParam, rcNull);
2613 
2614 
2615     if (buffer_size < sizeof (header.file_sig))
2616         return RC (rcFS, rcFile, rcIdentifying, rcBuffer, rcInsufficient);
2617 
2618     if (memcmp (buffer, &const_header_sra.file_sig, sizeof const_header.file_sig ) != 0)
2619         return RC (rcFS, rcFile, rcIdentifying, rcFile, rcWrongType);
2620 
2621     if (buffer_size < sizeof header.file_sig + sizeof header.byte_order)
2622         return 0;
2623 
2624     count = buffer_size > sizeof header ? sizeof header : buffer_size;
2625 
2626     memmove (&header, buffer, count);
2627 
2628     if (header.byte_order == const_header.byte_order)
2629         byte_swapped = false;
2630 
2631     else if (header.byte_order == const_bswap_header.byte_order)
2632         byte_swapped = true;
2633 
2634     else
2635         return RC (rcFS, rcFile, rcIdentifying, rcFile, rcOutoforder);
2636 
2637     if (buffer_size < sizeof (header))
2638         return 0;
2639 
2640     assert (sizeof (header.version) == 4);
2641     if (byte_swapped)
2642         header.version = bswap_32(header.version);
2643 
2644     if ((header.version <= 0) || (header.version > eCurrentVersion))
2645         return RC (rcKrypto, rcFile, rcClassifying, rcFile, rcBadVersion);
2646 
2647     return 0;
2648 }
2649 
2650 /* end of file encfile.c */
2651 
2652 
2653 
2654