 /* secure database
  *
  * This file is part of the CRYPTO BONE Project
  * File: initdatabase.c
  * Version  : 2.2 (ALL-IN-ONE)
  * License  : BSD-3-Clause
  * Date:      12 Aug 2026
  *
  * Summary:
  * 
  * Copyright (c) 2015-2026
  *	Ralf Senderek, Ireland.  All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
  * are met:
  * 1. Redistributions of source code must retain the above copyright
  *    notice, this list of conditions and the following disclaimer.
  * 2. Redistributions in binary form must reproduce the above copyright
  *    notice, this list of conditions and the following disclaimer in the
  *    documentation and/or other materials provided with the distribution.
  * 3. All advertising materials mentioning features or use of this software
  *    must display the following acknowledgement:
  *	   This product includes software developed by Ralf Senderek.
  *
  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  *
  *****************************************************/



#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>

#include <cryptlib/cryptlib.h>
#include <bsd/string.h>
#include "armor.h"

#define DEBUG 0

#define MAXINPUT 250000
#define MAXWORD 66668  /* max number of bytes for a parameter through socket */

#define BYTE unsigned char

#define DATABASEFILE "/usr/lib/cryptobone/database"
#define DATABASEBACKUP "/usr/lib/cryptobone/database.back"
#define INTERNAL_BUFFER_SIZE MAXINPUT+1024
#define MAXBUF  2*MAXINPUT 
#define KEYLEN 101

#define cryptUser CRYPT_UNUSED
#define MAXBOOTSECONDS 120

int main(void) {

     unsigned char plaintext[MAXINPUT] ;
     unsigned char input[MAXBUF];
     unsigned char output[MAXBUF*4/3];    /* output holds base64 encoded data and exceeds input */

     unsigned char password[KEYLEN]; 
     int  passwordLength = 0;
     int  status = 0;
     int  num = 0;
     unsigned char *sptr ;
     FILE *fp;


/***************************************************************************************/
int get_masterkey(){
      if (strlen(password) == 40) {
           return 0;
      }
      return -1;
}

/***************************************************************************************/
int aes_encrypt(){
      /*
       * Reads from plaintext and writes the cryptogram to input and 
       * base64-encoded to output.
       * output is stored in the database file
       */

      FILE *fd;
      int newfd;

      if (get_masterkey() != 0){
           printf("Error: no key, encryption failed.");
	   return 4;
      }

      if (strlen(plaintext) >= MAXINPUT){
	   return 2;
      }

      int bytesCopied = 0;
      int plaintextLength = strlen(plaintext);

      if (plaintextLength == 0){
           printf ("Error: plaintext is empty.");
	   return 3;
      }


      CRYPT_ENVELOPE cryptEnvelope;
      

      status = cryptCreateEnvelope( &cryptEnvelope, cryptUser, CRYPT_FORMAT_CRYPTLIB );
      if( status != CRYPT_OK ){
          printf ("Error: Cryptlib enveloping.");
	  return 3;
      }

      /* set internal buffer size */
      status = cryptSetAttribute( cryptEnvelope, CRYPT_ATTRIBUTE_BUFFERSIZE, INTERNAL_BUFFER_SIZE ); 
 
      /* Add the password */
      passwordLength = strlen(password);
      if (passwordLength < 20){
           printf ("Error: invalid key, encryption failed.");
	   return 3;
      }
      if (passwordLength > 64){
           printf ("Error: invalid key, encryption failed.");
	   return 3;
      }
      
      if (DEBUG)  printf("Using password of length %i\n",passwordLength);
      status = cryptSetAttributeString( cryptEnvelope, CRYPT_ENVINFO_PASSWORD, password, passwordLength );

      cryptSetAttribute( cryptEnvelope, CRYPT_ENVINFO_DATASIZE, plaintextLength );

      status = cryptPushData( cryptEnvelope, plaintext, plaintextLength, &bytesCopied );
      status = cryptFlushData( cryptEnvelope );
      
      /* create cryptogram in buffer input */
      status = cryptPopData( cryptEnvelope, input, MAXBUF, &bytesCopied );
     
      if (DEBUG)  printf("encrypted Buffer has %i bytes\n", bytesCopied);
      
      if (status == CRYPT_OK){
            status = base64encode(output, 2*MAXBUF, &num, input, bytesCopied, CRYPT_CERTTYPE_NONE);
            if (DEBUG)  printf("base64 input %i bytes output %i bytes \n", bytesCopied, num);

            /* set '\0' at pos num in output */
            output[num] = '\0';

            /* make backup of the database file */
            fd = popen("/bin/touch /usr/lib/cryptobone/database", "r");
            if (fd != NULL ){
                 pclose(fd);
	    }
            		 
            fd = popen("/bin/cp /usr/lib/cryptobone/database /usr/lib/cryptobone/database.back", "r");
            if (fd != NULL ){
                 pclose(fd);
                 if (DEBUG) printf("backup created");

                 newfd = open(DATABASEBACKUP,O_RDWR);
                 fchmod(newfd,S_IRUSR | S_IWUSR); 
                 close(newfd);
	    }

	    /* write result to file */
            fp = fopen(DATABASEFILE,"w");
            if (!fp){
                 printf("Error: database is not available");
	         return 5;
            }
            else {
                 fputs(output,fp);
                 fclose(fp);
                 /* change file permissions to 0600 */
                 newfd = open(DATABASEFILE,O_RDWR);
                 fchmod(newfd,S_IRUSR | S_IWUSR); 
                 close(newfd);
            }
      }
      status = cryptDestroyEnvelope( cryptEnvelope );
      if( status != CRYPT_OK ){
          printf("Error: Envelope cannot be destroyed.");
	  return 4;
      }
      
      return 0;
}

/***************************************************************************************/
int this_is_boottime(){
     
     char content[5];

     /* read bootswitch file */
     fp = fopen("/usr/lib/cryptobone/bootswitch","r");
     if (!fp){
           return -1;
     }
     else {
          sptr = fgets(content,5,fp);
          fclose(fp);
	  if ((sptr != NULL) && (atoi(content) < MAXBOOTSECONDS)) {
	        /* it's boot time */
	        return 0;
	  }	
     }
     return -1;
}

/*****MAIN***************************************************************/

        /* try to read the password if this is boot time */
        if (this_is_boottime() == 0) {
             fp = fopen("/usr/lib/cryptobone/keys/master.key","r");
             if (!fp){
                  printf ("Error: master key is not readable");
	          fclose(fp);
	          exit(1);
             }
             else {
                  sptr = fgets(password,41,fp);
	          password[41] = '\0';
                  fclose(fp);
		  printf ("Reading of master key succeeded\n");
	          /* check if the database exists, create a new one if not */
	          FILE *fptr = fopen(DATABASEFILE, "r");
	          if (! fptr) {
	              printf ("Database INITIALISATION necessary!\n");
	              strlcpy(plaintext, "cryptobone:version 2.2\n", MAXINPUT);
		      printf("plaintext: %s",plaintext);
	              status = cryptInit();
		      printf("cryptInit status = %d\n", status);
		      status = cryptAddRandom( NULL, CRYPT_RANDOM_SLOWPOLL );
		      printf("cryptAddRandom status = %d\n", status);
	              aes_encrypt();
		      printf("aes_encrypt done.\n");
		      status = cryptEnd();
		      printf("cryptEnd status = %d\n", status);
		      printf ("Database INIT finished.\n");
	          }
	          else {
	              printf ("Database exists!\n");
	              fclose(fptr);
	          }
             }
        }
	else {
	     /* illegal start after boot time */
             printf ("Error: boot time has elapsed. Terminating database init ... \n");
             exit(6);
	}
     
        /* ZEROIZE password and plaintext */
        for (int i=0; i < KEYLEN; i++){
             password[i] = '\0';
        }

        exit(0);
}
