/* Written by Kelce Wilson, Jan 2003 and Jan 2009, using FIPS PUB 180-1, dated 17 April 1995 and FIPS PUB 180-2 with change notice, dated 1 August 2002. Performs the NSA's Secure Hash Algorithm-1 (SHA-1) on files up to 512Mb and SHA-2, 512 bit digest (SHA-512) on files up to 2Gb. */ /* You have a License to use this source code in your own projects, provided that you adhere to the following terms: You do not use this software with any blockchain that competes with PEDDaL, whether for generating or verifying a document record. You do not use this software in furtherance of any criminal activity. Use of this software is entirely at your own risk. There are no warranties of merchantability, fitness for a particular purpose, title, or noninfringement. In the event that any disputes arise, the original author of this software selects both venue and choice of law. You have no recourse against the author(s), owner (if different), or any other entity that provided you a copy of this source code, for any injuries sustained as a result of using this software. Liquidated damages for submitting a record to a PEDDaL competitor will be the greater of US $10 per record, or three times the cost of having submitted the same number of records to PEDDaL. Either the original author or owner (if different) may waive any term in this license for any person or entity, without incurring any obligation to waive any terms for any other person or entity. Subsequent contributors to authorship of this software have no right to waive license terms or receive liquidated damages. If any terms of this License are found to be unenforceable, illegal, or waived, all other terms remain in force. */ //--------------------------------------------------------------------------- #include #include #include #include // Specific to Borland C++ Builder. Remove for other compilers #include // Specific to Borland C++ Builder. Remove for other compilers #pragma hdrstop // Specific to Borland C++ Builder. Remove for other compilers #pragma argsused // Specific to Borland C++ Builder. Remove for other compilers //--------------------------------------------------------------------------- // Function Prototypes int kw_sha12_shell(int argc, char **argv); // Shell program to call the real algorithms int octet_to_pdl_file_name(char *file_name, int value, int digits); // Functions needed for SHA-1 int kw_sha1(unsigned int *message_digest, char *file_name); // SHA-1 algorithm unsigned int Sn(unsigned int X, int n); unsigned int Ft(unsigned int B, unsigned int C, unsigned int D, int t); unsigned int Kt(int t); // Functions needed for SHA-2 __int64 kw_sha2(unsigned __int64 *message_digest, char *file_name); // SHA-512 algorithm unsigned __int64 SIG_0(unsigned __int64 x); unsigned __int64 SIG_1(unsigned __int64 x); unsigned __int64 sig_0(unsigned __int64 x); unsigned __int64 sig_1(unsigned __int64 x); unsigned __int64 Ch(unsigned __int64 x, unsigned __int64 y, unsigned __int64 z); unsigned __int64 Maj(unsigned __int64 x, unsigned __int64 y, unsigned __int64 z); unsigned __int64 ROTR(unsigned __int64 x, int n); // End of functions needed for SHA-2 void show_help(); //--------------------------------------------------------------------------- int main(int argc, char **argv){ int result=kw_sha12_shell(argc,argv); printf("\nDone. Press any key to quit.\n"); // ************************************************** // The following section is specific to Borland's BCB compiler // Prevent DOS console window from vanishing getch(); // End of section specific to Borland's BCB compiler // ************************************************** return result; } //--------------------------------------------------------------------------- int kw_sha12_shell(int argc, char **argv){ // Shell program to call SHA-1 and SHA-2 functions. Runs as command-line input application char input_file_name[200],output_file_name[200],octet_file_name[12]; int result_32; __int64 result_64; // Declare the message digests as static to set them to all 0's upon initialization. // Otherwise they may contain random numbers static unsigned int message_digest_1[5]; // 160 bits is 5 32-bit integers static unsigned __int64 message_digest_2[8]; // 512 bits is 8 64-bit integers FILE *output_file,*test_file; time_t timestamp; int i,file_name_length; int version = 1; // Look at command line arguments for file name(s) printf("\n"); if((argc<2)||(argc>3)){ // Incorrect number of command line arguments printf("\n"); printf(" *** Syntax Error ***\n\n Incorrect number of command-line arguments.\n\n"); show_help(); return 0; } if(argc==2){ if(!strcmp(argv[1],"-h")){ show_help(); return 0; } } // Get filename, ifnore all other command line arguments file_name_length=strlen(argv[1]); if(file_name_length<200){ // Ensure file name is not too long strcpy(input_file_name,argv[1]); }else{ printf(" ***** FATAL ERROR *****\n Input file name exceeds the limit of 200 characters:\n%s\n",argv[1]); return 0; } // Test file to make sure it opens if((test_file=fopen(input_file_name,"rb"))==NULL){ printf(" ***** ERROR *****\n Cannot open file: %s for reading.\n",input_file_name); return 0; } fclose(test_file); // Do SHA-1 first result_32=kw_sha1(message_digest_1,input_file_name); if(result_32<0){ printf(" ***** FATAL ERROR *****\n Algorithm failed\n\n",argv[1]); switch (result_32) { case -1 : printf("File error.\n"); break; case -2 : printf("File exceeds limit of 512Mb.\n"); break; case -3 : printf("Logic error. Please contact program author.\n"); break; default :; } return result_32; } printf("SHA-1 message digest:\n"); for(i=0;i<5;i++){ printf("%08.8X ",message_digest_1[i]); } printf("\n\n"); // Do SHA-512 second result_64=kw_sha2(message_digest_2,input_file_name); if(result_64<0){ printf(" ***** FATAL ERROR *****\n Algorithm failed\n\n",argv[1]); switch (result_64) { case -1 : printf("File error.\n"); break; case -2 : printf("File exceeds limit of 2Gb.\n"); break; case -3 : printf("Logic error. Please contact program author.\n"); break; default :; } return result_64; } printf("SHA-512 message digest:\n"); for(i=0;i<8;i++){ printf("%016.16LX ",message_digest_2[i]); if(i==3){ printf("\n"); } } printf("\n"); // Determine record file name from first octet of SHA-1 octet_to_pdl_file_name(octet_file_name,message_digest_1[0],8); // ensure file name is cleared for(i=0;i<200;i++){ output_file_name[i]=0; } // Ensure only 12 characters. octet_file_name may have 3 mystery extra for(i=0;i<12;i++){ output_file_name[i]=octet_file_name[i]; } // Write output file if requested if((output_file=fopen(output_file_name,"wt"))==NULL){ printf(" ***** ERROR *****\n Cannot open file: %s for writing.\n",output_file_name); }else{ /* ***** NOTE: ***** The output file is written as ACII Text output, which represents 4 bits as 1 byte This will prevent problems with firewalls, virus-checkers, and other data transfer limitations, which might react badly to files possessing raw binary sequences appearing to contain executable sequences. Also, the text format makes the records easier to review by most users. Character fields for a PEDDaL(R) blockchain record: Characters 1-128 (128), representing 512 bits: SHA-512 message digest; Characters 129-168 (40), representing 160 bits: SHA-1 message digest; Characters 169-174 (6), representing 24 bits: software version identification code; Characters 175-182 (8), representing 32 bits:: timestamp in clear text; Characters 183-256, (74), remainder of record: Zero padding for reserved fields; Characters 251-256 (6), representing 24 bits, will be used for a record index when the record is added into the PEDDaLŽ blockchain. Characters 169Bits 673-696 (24): identification code for hash functions and software version; */ // put in SHA-512 hash for(i=0;i<8;i++){ fprintf(output_file,"%016.16LX",message_digest_2[i]); } // put in SHA-1 hash for(i=0;i<5;i++){ fprintf(output_file,"%08.8X",message_digest_1[i]); } fprintf(output_file,"%06.6X",version); timestamp=time(NULL); // timestamp from running this program (if used - see above) fprintf(output_file,"%08.8X",timestamp); // PEDDaL acknowledged time - leave blank here fprintf(output_file,"%08.8X",0); // Pad remainder of record with zeros for(i=0;i<8;i++){ fprintf(output_file,"%08.8X",0); } // Finish last 2 characters fprintf(output_file,"%02.2X",0); fclose(output_file); } return result_32; } //--------------------------------------------------------------------------- int octet_to_pdl_file_name(char *file_name, int value, int digits){ // This is needed to compensate for IntToHex not padding with leading zeros int i,hex_digit[8],result; unsigned int temp_value,shift_value; if(digits!=8){return -1;} // ERROR this only works on hex octets // Extract individual hex digits from the input value temp_value=(unsigned)value; shift_value=temp_value; shift_value=shift_value>>28; // save only left-most hex digit of 32-bit bunber hex_digit[0]=(signed)shift_value; // assign left-most hex digit shift_value=shift_value<<28; // return to left-most position to remove it temp_value=temp_value-shift_value; shift_value=temp_value; shift_value=shift_value>>24; // save only the second left-most hex digit hex_digit[1]=(signed)shift_value; // assign second left-most hex digit shift_value=shift_value<<24; // return to left-most position to remove it temp_value=temp_value-shift_value; shift_value=temp_value; shift_value=shift_value>>20; // now the third hex digit hex_digit[2]=(signed)shift_value; shift_value=shift_value<<20; temp_value=temp_value-shift_value; shift_value=temp_value; shift_value=shift_value>>16; // now the fourth hex digit hex_digit[3]=(signed)shift_value; shift_value=shift_value<<16; temp_value=temp_value-shift_value; shift_value=temp_value; shift_value=shift_value>>12; // now the fifth hex digit hex_digit[4]=(signed)shift_value; shift_value=shift_value<<12; temp_value=temp_value-shift_value; shift_value=temp_value; shift_value=shift_value>>8; // now the sixth hex digit hex_digit[5]=(signed)shift_value; shift_value=shift_value<<8; temp_value=temp_value-shift_value; shift_value=temp_value; shift_value=shift_value>>4; // now the seventh hex digit hex_digit[6]=(signed)shift_value; shift_value=shift_value<<4; temp_value=temp_value-shift_value; hex_digit[7]=(signed)temp_value; // only right-most one is left, now // Convert each hex digit into ASCII for(i=0;imax_file_size){ return file_too_large; } if(file_length_bytes==0){ return file_error; } file_length_bits=8*file_length_bytes; fclose(input_file); // Close and re-open file to reset pointer location input_file=fopen(file_name,"rb"); pad_length_bytes=64-file_length_bytes%64; if(pad_length_bytes<9){ second_block_pad=true; pad_length_bytes=pad_length_bytes+64; } if(pad_length_bytes==64){ second_block_pad=true; } do{ num_read=fread(block,1,64,input_file); read_count=read_count+num_read; if(read_count==file_length_bytes){ start_pad=true; // End of the file reached as expected } if(read_count>file_length_bytes){ return file_error; // Read past expected end of file } if((num_read<64)&&(!start_pad)){ return file_error; // Read stopped prior to expected end of file } num_whole_words=num_read/4; if(num_whole_words*4==num_read){ all_whole_words=true; // Easy assembly of words }else{ all_whole_words=false; // More complicated assembly of words } for(i=0;i0){ return ((X<>(32-n))); }else{ return X; } } //--------------------------------------------------------------------------- unsigned int Ft(unsigned int B, unsigned int C, unsigned int D, int t){ unsigned int F; if(t<0){t=-t;} t=t%80; if(t<=19){ F=(B&C)^((~B)&D); } if((t>=20)&&(t<=39)){ F=B^C^D; } if((t>=40)&&(t<=59)){ F=(B&C)^(B&D)^(C&D); } if(t>=60){ F=B^C^D; } return F; } //--------------------------------------------------------------------------- unsigned int Kt(int t){ unsigned int K; if(t<0){t=-t;} t=t%80; if(t<=19){K=0x5A827999;} if((t>=20)&&(t<=39)){K=0x6ED9EBA1;} if((t>=40)&&(t<=59)){K=0x8F1BBCDC;} if(t>=60){K=0xCA62C1D6;} return K; } //--------------------------------------------------------------------------- __int64 kw_sha2(unsigned __int64 *message_digest, char *file_name){ /* Performs the NSA's Secure Hash Algorithm-2 for a 512-bit message digest written by Kelce Wilson, Jan 2009. *** This version only works on files up to 2Gb in size **** Exact maximum length = 2,147,483,647 bytes. This is due to the use of ftell to determine padding. ftell returns a 32-bit integer (possibly signed?). All addition operations using 64-bit integers are performed modulo-64. Return conditon Return value success the number of bytes hashed file i/o error -1 file too large -2 logic error -3 */ bool second_block_pad,all_whole_words,start_pad; unsigned char pad_byte_1; unsigned static char block[128]; // 1024 bits is 128 8-bit bytes FILE *input_file; int file_error,file_too_large,logic_error; // error return values int i,i8,j,t,num_read,pad_length_bytes,num_whole_words,straggler_bytes, num_bytes_padded,num_words_filled; unsigned int file_length_bytes,read_count,max_file_size,pad_int_1; unsigned __int64 file_length_bits,temp_word,a,b,c,d,e,f,g,h,T1,T2; unsigned __int64 message_word[16]; // 1024 bits is 16 64-bit ints unsigned __int64 W[80]; unsigned __int64 K[80]={ 0x428a2f98d728ae22, 0x7137449123ef65cd, 0xb5c0fbcfec4d3b2f, 0xe9b5dba58189dbbc, 0x3956c25bf348b538, 0x59f111f1b605d019, 0x923f82a4af194f9b, 0xab1c5ed5da6d8118, 0xd807aa98a3030242, 0x12835b0145706fbe, 0x243185be4ee4b28c, 0x550c7dc3d5ffb4e2, 0x72be5d74f27b896f, 0x80deb1fe3b1696b1, 0x9bdc06a725c71235, 0xc19bf174cf692694, 0xe49b69c19ef14ad2, 0xefbe4786384f25e3, 0x0fc19dc68b8cd5b5, 0x240ca1cc77ac9c65, 0x2de92c6f592b0275, 0x4a7484aa6ea6e483, 0x5cb0a9dcbd41fbd4, 0x76f988da831153b5, 0x983e5152ee66dfab, 0xa831c66d2db43210, 0xb00327c898fb213f, 0xbf597fc7beef0ee4, 0xc6e00bf33da88fc2, 0xd5a79147930aa725, 0x06ca6351e003826f, 0x142929670a0e6e70, 0x27b70a8546d22ffc, 0x2e1b21385c26c926, 0x4d2c6dfc5ac42aed, 0x53380d139d95b3df, 0x650a73548baf63de, 0x766a0abb3c77b2a8, 0x81c2c92e47edaee6, 0x92722c851482353b, 0xa2bfe8a14cf10364, 0xa81a664bbc423001, 0xc24b8b70d0f89791, 0xc76c51a30654be30, 0xd192e819d6ef5218, 0xd69906245565a910, 0xf40e35855771202a, 0x106aa07032bbd1b8, 0x19a4c116b8d2d0c8, 0x1e376c085141ab53, 0x2748774cdf8eeb99, 0x34b0bcb5e19b48a8, 0x391c0cb3c5c95a63, 0x4ed8aa4ae3418acb, 0x5b9cca4f7763e373, 0x682e6ff3d6b2b8a3, 0x748f82ee5defb2fc, 0x78a5636f43172f60, 0x84c87814a1f0ab72, 0x8cc702081a6439ec, 0x90befffa23631e28, 0xa4506cebde82bde9, 0xbef9a3f7b2c67915, 0xc67178f2e372532b, 0xca273eceea26619c, 0xd186b8c721c0c207, 0xeada7dd6cde0eb1e, 0xf57d4f7fee6ed178, 0x06f067aa72176fba, 0x0a637dc5a2c898a6, 0x113f9804bef90dae, 0x1b710b35131c471b, 0x28db77f523047d84, 0x32caab7b40c72493, 0x3c9ebe0a15c9bebc, 0x431d67c49c100d4c, 0x4cc5d4becb3e42b6, 0x597f299cfc657e2a, 0x5fcb6fab3ad6faec, 0x6c44198c4a475817}; unsigned __int64 H[8]={ 0x6a09e667f3bcc908, 0xbb67ae8584caa73b, 0x3c6ef372fe94f82b, 0xa54ff53a5f1d36f1, 0x510e527fade682d1, 0x9b05688c2b3e6c1f, 0x1f83d9abfb41bd6b, 0x5be0cd19137e2179}; file_error=-1; file_too_large=-2; logic_error=-3; max_file_size=2147483647; second_block_pad=false; start_pad=false; pad_byte_1=128; pad_int_1=2147483648; read_count=0; num_bytes_padded=0; // Open and read input file if((input_file=fopen(file_name,"rb"))==NULL){ return file_error; } fseek(input_file,0L,SEEK_END); file_length_bytes=ftell(input_file); if(file_length_bytes>max_file_size){ return file_too_large; } if(file_length_bytes==0){ return file_error; } file_length_bits=8*file_length_bytes; fclose(input_file); // Close and re-open file to reset pointer location input_file=fopen(file_name,"rb"); pad_length_bytes=128-file_length_bytes%128; if(pad_length_bytes<17){ second_block_pad=true; pad_length_bytes=pad_length_bytes+128; } if(pad_length_bytes==128){ second_block_pad=true; } do{ num_read=fread(block,1,128,input_file); read_count=read_count+num_read; if(read_count==file_length_bytes){ start_pad=true; // End of the file reached as expected } if(read_count>file_length_bytes){ return file_error; // Read past expected end of file } if((num_read<128)&&(!start_pad)){ return file_error; // Read stopped prior to expected end of file } num_whole_words=num_read/8; if(num_whole_words*8==num_read){ all_whole_words=true; // Easy assembly of words }else{ all_whole_words=false; // More complicated assembly of words } for(i=0;i>7); } //--------------------------------------------------------------------------- unsigned __int64 sig_1(unsigned __int64 x){ return (ROTR(x,19) ^ ROTR(x,61) ^ x>>6); } //--------------------------------------------------------------------------- unsigned __int64 Ch(unsigned __int64 x, unsigned __int64 y, unsigned __int64 z){ return ((x & y) ^ ((~x) & z)); } //--------------------------------------------------------------------------- unsigned __int64 Maj(unsigned __int64 x, unsigned __int64 y, unsigned __int64 z){ return ((x & y) ^ (x & z) ^ (y & z)); } //--------------------------------------------------------------------------- unsigned __int64 ROTR(unsigned __int64 x, int n){ n=n%64; if(n>0){ return ((x>>n)|(x<<(64-n))); }else{ return x; } } //--------------------------------------------------------------------------- void show_help(){ char octet_file_name[12]; printf(" ** PEDDaL_Record_Generator_CL help **\n\n"); printf(" syntax: pdl_gen input_file_name \n"); printf("\nThis program creates a PEDDaL(R) blockchain record, suitable for \nsubmission to the PEDDaL(R) blockchain.\n\n"); printf("It generates 512-bit Secure Hash Algorithm-2 (SHA-512) and SHA-1 message \n"); printf("digests, as described by the first method given in FIPS PUB 180-1, dated \n"); printf("17 April 1995, and FIPS PUB 180-2, dated 1 August 2002.\n"); printf("\nThe output record will be named as the first octet of the SHA-1,\n with the extension .pdl.\n"); printf("\nThe message digests will be printed to the screen.\n"); printf("\n"); printf("This version only works on files up to 512Mb in length (536,870,911 bytes).\n"); printf("\nPossible return values and their associated conditions:\n"); printf(" # bytes hashed success\n -1 file i/o error\n"); printf(" -2 file exceeds 512Mb (536,870,911 bytes; 4,294,967,288 bits)\n"); printf(" -3 logic error\n"); printf("\n Written by Kelce Wilson, January 2009 (rev. Jan 2022).\n\n"); return; } //---------------------------------------------------------------------------