// ======================================================================================================== // ======================================================================================================== // **************************************** device_regeneration.c ***************************************** // ======================================================================================================== // ======================================================================================================== // //-------------------------------------------------------------------------------- // Company: IC-Safety, LLC and University of New Mexico // Engineer: Professor Jim Plusquellic // Exclusive License: IC-Safety, LLC // Copyright: Univ. of New Mexico //-------------------------------------------------------------------------------- #include #include "common.h" #include "device_hardware.h" #include "device_common.h" #include "device_regen_funcs.h" #include "commonDB_RT_PUFCash.h" // ====================== DATABASE STUFF ========================= #include #include "commonDB.h" extern int usleep (__useconds_t __useconds); extern int getpagesize (void) __THROW __attribute__ ((__const__)); // ======================================================================================================== // ======================================================================================================== // Alice calls this at startup to get the list of IPs for TTPs and Customers. It gets the current list from // the Bank, and then queries each client for it's capability. void AliceGetClient_IPs(int max_string_len, SRFHardwareParamsStruct *SHP_ptr, char *Bank_IP, int port_number, ClientInfoStruct **Client_CIArr_ptr, int max_connect_attempts, int ip_length, char *My_IP, int *num_TTPs_ptr, int *num_customers_ptr) { int my_IP_pos, num_clients, exclude_self, set_num; unsigned char *Alice_session_key; int Bank_socket_desc; int gen_session_key; int start_index, is_TTP; char *check_IP; printf("AliceGetClient_IPs(): START!\n"); fflush(stdout); #ifdef DEBUG #endif // Once for TTPs and once for customers. ONLY SUPPORTED FOR TWO iterations. If more, YOU MUST set start_index // below. start_index = 0; for ( set_num = 0; set_num < 2; set_num++ ) { while ( OpenSocketClient(max_string_len, Bank_IP, port_number, &Bank_socket_desc) < 0 ) { printf("INFO: Alice waiting to connect to Bank for TTP information!\n"); fflush(stdout); usleep(200000); } // Tell Bank we want the TTP or customer device information that it knows about. if ( set_num == 0 && SockSendB((unsigned char *)"ALICE-GET-TTP-IPS", strlen("ALICE-GET-TTP-IPS") + 1, Bank_socket_desc) < 0 ) { printf("ERROR: AliceGetClient_IPs(): Failed to send 'ALICE-GET-TTP-IPS' to Bank!\n"); exit(EXIT_FAILURE); } if ( set_num == 1 && SockSendB((unsigned char *)"ALICE-GET-CUSTOMER-IPS", strlen("ALICE-GET-CUSTOMER-IPS") + 1, Bank_socket_desc) < 0 ) { printf("ERROR: AliceGetClient_IPs(): Failed to send 'ALICE-GET-CUSTOMER-IPS' to Bank!\n"); exit(EXIT_FAILURE); } // Generate session key with Bank. gen_session_key = 1; if ( KEK_ClientServerAuthenKeyGen(max_string_len, SHP_ptr, Bank_socket_desc, gen_session_key) == 0 ) exit(EXIT_FAILURE); Alice_session_key = SHP_ptr->SE_final_key; SHP_ptr->SE_final_key = NULL; // When called to get TTP IP info, set chip_IP to NULL so an error check is NOT performed in the GetClient_IPs routine. if ( set_num == 0 ) { check_IP = NULL; is_TTP = 1; } else { check_IP = My_IP; is_TTP = 0; } exclude_self = 0; num_clients = GetClient_IPs(max_string_len, SHP_ptr, Alice_session_key, Bank_socket_desc, Client_CIArr_ptr, max_connect_attempts, ip_length, check_IP, &my_IP_pos, &exclude_self, start_index, is_TTP); if ( set_num == 0 ) { *num_TTPs_ptr = num_clients; // We must set the start_index to a point AFTER in TTP elements. start_index = *num_TTPs_ptr; printf("AliceGetClient_IPs(): Bank returned %d TTP IPs!\n", *num_TTPs_ptr); fflush(stdout); #ifdef DEBUG #endif } else { *num_customers_ptr = num_clients; // WARNING: We do NOT set start_index here because we are done after two iterations. printf("AliceGetClient_IPs(): Bank returned %d Customer IPs!\n", *num_customers_ptr); fflush(stdout); #ifdef DEBUG #endif } if ( Alice_session_key != NULL ) free(Alice_session_key); close(Bank_socket_desc); } printf("AliceGetClient_IPs(): DONE!\n"); fflush(stdout); #ifdef DEBUG #endif return; } // ======================================================================================================== // ======================================================================================================== // ======================================================================================================== SRFHardwareParamsStruct SHP; int main(int argc, char *argv[]) { volatile unsigned int *CtrlRegA; volatile unsigned int *DataRegA; unsigned int ctrl_mask; char *MyName; char *Bank_IP; char *My_IP; char *IP_list_filename; char *temp_str; int Bank_socket_desc = 0; int port_number; int nonce_base_address; int num_eCt_nonce_bytes; int num_KEK_authen_nonce_bytes; int fix_params; int num_sams; int num_PIs; int num_POs; int DUMP_BITSTRINGS; int DEBUG_FLAG; int PCR_or_PBD_or_PO; // The PL-side TRNG_LFSR is 64 bits. Note that we currently only suport loading the low-order 8-bits of the seed register below. unsigned char TRNG_LFSR_seed; // ====================== DATABASE STUFF ========================= sqlite3 *DB_Challenges; int rc; char *DB_name_Challenges; Allocate1DString(&DB_name_Challenges, MAX_STRING_LEN); int use_database_chlngs; char *Netlist_name; char *Synthesis_name; char *ChallengeSetName; int design_index; int num_PIs_DB, num_POs_DB; int ChallengeGen_seed; // Trust protocol sqlite3 *DB_Trust_AT; char *DB_name_Trust_AT; Allocate1DString(&DB_name_Trust_AT, MAX_STRING_LEN); // PUF-Cash V3.0 protocol sqlite3 *DB_PUFCash_V3; char *DB_name_PUFCash_V3; Allocate1DString(&DB_name_PUFCash_V3, MAX_STRING_LEN); float command_line_SC; Allocate1DString(&MyName, MAX_STRING_LEN); Allocate1DString(&My_IP, MAX_STRING_LEN); Allocate1DString(&Bank_IP, MAX_STRING_LEN); // ====================================================================================================================== // COMMAND LINE // ====================================================================================================================== if ( argc != 4 ) { printf("Parameters: MyName (Alice/Bob/Jim/Cyrus/George) -- Device IP (192.168.1.10) -- Bank IP (192.168.1.20)\n"); exit(EXIT_FAILURE); } strcpy(MyName, argv[1]); strcpy(My_IP, argv[2]); strcpy(Bank_IP, argv[3]); fix_params = 0; num_sams = 4; PCR_or_PBD_or_PO = 0; command_line_SC = 1.0; // Sanity checks if ( fix_params != 0 && fix_params != 1 ) { printf("ERROR: 'fix_params' MUST be 0 or 1!\n"); exit(EXIT_FAILURE); } if ( num_sams != 1 && num_sams != 4 && num_sams != 8 && num_sams != 16 ) { printf("ERROR: 'num_sams' MUST be 1, 4, 8 or 16!\n"); exit(EXIT_FAILURE); } if ( PCR_or_PBD_or_PO != 0 && PCR_or_PBD_or_PO != 1 && PCR_or_PBD_or_PO != 2 ) { printf("ERROR: 'PCR_or_PBD_or_PO' MUST be 0, 1 or 2!\n"); exit(EXIT_FAILURE); } // Upper limit is arbitrary -- they never get this big -- 3.2 looks to be the max. if ( command_line_SC <= 0.0 || command_line_SC > (float)MAX_SCALING_VALUE ) { printf("ERROR: 'command_line_SC' MUST be >= 0.0 and <= %f -- FIX ME!\n", (float)MAX_SCALING_VALUE); exit(EXIT_FAILURE); } Allocate1DString(&IP_list_filename, MAX_STRING_LEN); Allocate1DString(&temp_str, MAX_STRING_LEN); Allocate1DString((char **)(&Netlist_name), MAX_STRING_LEN); Allocate1DString((char **)(&Synthesis_name), MAX_STRING_LEN); Allocate1DString((char **)(&ChallengeSetName), MAX_STRING_LEN); // ====================================================== PARAMETERS ==================================================== strcpy(DB_name_Challenges, "Challenges.db"); strcpy(Netlist_name, "SR_RFM_V4_TDC"); strcpy(Synthesis_name, "SRFSyn1"); strcpy(ChallengeSetName, "Master1_OptKEK_TVN_0.00_WID_1.75"); strcpy(DB_name_Trust_AT, "AuthenticationToken.db"); strcpy(DB_name_PUFCash_V3, "PUFCash_V3.db"); // Must be set to 0 until I fully integrate this into all of the primitives. use_database_chlngs = 0; ChallengeGen_seed = 1; // SET TO WHATEVER bitstream you program with. // int my_bitstream; // my_bitstream = 0; char AES_IV[AES_IV_NUM_BYTES] = {0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF}; // The PL-side TRNG_LFSR is 64 bits. TRNG_LFSR_seed = 1; // NOTE: ASSUMPTION: // NUM_XOR_NONCE_BYTES <= num_eCt_nonce_bytes <= SE_TARGET_NUM_KEY_BITS/8 <= NUM_REQUIRED_PNDIFFS/8 // 8 16 32 256 // We always use 8 bytes for the XOR_nonce (NUM_XOR_NONCE_BYTES) to SelectParams. My plan is to use 16 byte nonces // (num_eCt_nonce_bytes), 32 bytes AES session keys (256 bits for SE_TARGET_NUM_KEY_BITS) and NUM_REQUIRED_PNDIFFS are always // 2048/16 = 256. num_eCt_nonce_bytes = ECT_NUM_BYTES; num_KEK_authen_nonce_bytes = KEK_AUTHEN_NUM_NONCE_BITS/8; // Base address, can be eliminated -- always 0. nonce_base_address = 0; port_number = 8888; // These depend on the function unit. For SR_RFM, it's 784 and 64 num_PIs = NUM_PIS; num_POs = NUM_POS; // Enable/disable debug information. DUMP_BITSTRINGS = 0; DEBUG_FLAG = 0; // ====================================================== PARAMETERS ==================================================== // Sanity check Trust protocols. We also assume that the SHA-3 hash input and output are the same size as the AK_A/MHK_As, // which must be the same size as the KEK key (since we use KEK_Regen() below to regenerate it). if ( HASH_IN_LEN_BITS != KEK_TARGET_NUM_KEY_BITS || HASH_OUT_LEN_BITS != KEK_TARGET_NUM_KEY_BITS ) { printf("ERROR: main(): HASH_IN_LEN_BITS %d MUST be equal to HASH_OUT_LEN_BIT %d MUST be equal to KEK_TARGET_NUM_KEY_BITS %d\n", HASH_IN_LEN_BITS, HASH_OUT_LEN_BITS, KEK_TARGET_NUM_KEY_BITS); exit(EXIT_FAILURE); } // Sanity check, constraint must be honored because of space allocations. // NUM_XOR_NONCE_BYTES <= num_eCt_nonce_bytes <= SE_TARGET_NUM_KEY_BITS/8 <= NUM_REQUIRED_PNDIFFS/8 // 8 16 32 256 if ( !(NUM_XOR_NONCE_BYTES <= num_eCt_nonce_bytes && num_eCt_nonce_bytes <= SE_TARGET_NUM_KEY_BITS/8 && SE_TARGET_NUM_KEY_BITS/8 <= NUM_REQUIRED_PNDIFFS/8) ) { printf("ERROR: Constraint violated: NUM_XOR_NONCE_BYTES %d <= num_eCt_nonce_bytes %d && \n\ num_eCt_nonce_bytes %d <= SE_TARGET_NUM_KEY_BITS/8 %d <= NUM_REQUIRED_PNDIFFS/8 %d\n", NUM_XOR_NONCE_BYTES, num_eCt_nonce_bytes, num_eCt_nonce_bytes, SE_TARGET_NUM_KEY_BITS/8, NUM_REQUIRED_PNDIFFS/8); exit(EXIT_FAILURE); } printf("Parameters: This Device IP %s\tBank IP %s\tFIX PARAMS %d\tNum Sams %d\tPCR/PBD/PO %d\n", My_IP, Bank_IP, fix_params, num_sams, PCR_or_PBD_or_PO); fflush(stdout); // The number of samples is set BELOW after CtrlRegA is given an address. ctrl_mask = 0; // For handling Ctrl-C. We MUST exit gracefully to keep the hardware from quitting at a point where the // fine phase of the MMCM is has not be set back to 0. If it isn't, then re-running this program will // likely fail because my local fine phase register (which is zero initially after a RESET) is out-of-sync // with the MMCM phase (which is NOT zero). signal(SIGINT, intHandler); // When we save output file, this tells us what we used. printf("PARAMETERS: PCR/PBD %d\tSE Target Num Bits %d\n\n", PCR_or_PBD_or_PO, SE_TARGET_NUM_KEY_BITS); fflush(stdout); // Open up the memory mapped device so we can access the GPIO registers. int fd = open("/dev/mem", O_RDWR|O_SYNC); if (fd < 0) { printf("ERROR: /dev/mem could NOT be opened!\n"); exit(EXIT_FAILURE); } // Add 2 for the DataReg (for an SpreadFactor of 8 bytes for 32-bit integer variables) DataRegA = (volatile unsigned int *)mmap(0, getpagesize(), PROT_READ|PROT_WRITE, MAP_SHARED, fd, GPIO_0_BASE_ADDR); CtrlRegA = DataRegA + 2; // ********************************************************************************************************** *CtrlRegA = ctrl_mask | (1 << OUT_CP_RESET); *CtrlRegA = ctrl_mask; usleep(10000); // Set the number of samples if ( num_sams == 1 ) ctrl_mask = (0 << OUT_CP_NUM_SAM1) | (0 << OUT_CP_NUM_SAM0); else if ( num_sams == 4 ) ctrl_mask = (0 << OUT_CP_NUM_SAM1) | (1 << OUT_CP_NUM_SAM0); else if ( num_sams == 8 ) ctrl_mask = (1 << OUT_CP_NUM_SAM1) | (0 << OUT_CP_NUM_SAM0); else if ( num_sams == 16 ) ctrl_mask = (1 << OUT_CP_NUM_SAM1) | (1 << OUT_CP_NUM_SAM0); else { printf("ERROR: Number of samples MUST be 1, 4, 8 or 16!\n"); exit(EXIT_FAILURE); } *CtrlRegA = ctrl_mask; // ====================== DATABASE STUFF ========================= rc = sqlite3_open(":memory:", &DB_Challenges); if ( rc != 0 ) { printf("Failed to open Challenge Database: %s\n", sqlite3_errmsg(DB_Challenges)); sqlite3_close(DB_Challenges); exit(EXIT_FAILURE); } printf("Reading filesystem database '%s' into memory!\n", DB_name_Challenges); fflush(stdout); if ( LoadOrSaveDb(DB_Challenges, DB_name_Challenges, 0) != 0 ) { printf("Failed to open and copy into memory '%s': ERR: %s\n", DB_name_Challenges, sqlite3_errmsg(DB_Challenges)); sqlite3_close(DB_Challenges); exit(EXIT_FAILURE); } // Get the PUFDesign parameters from the database. if ( GetPUFDesignParams(MAX_STRING_LEN, DB_Challenges, Netlist_name, Synthesis_name, &design_index, &num_PIs_DB, &num_POs_DB) != 0 ) { printf("ERROR: PUFDesign index NOT found for '%s', '%s'!\n", Netlist_name, Synthesis_name); exit(EXIT_FAILURE); } // Sanity check if ( num_PIs_DB != num_PIs || num_POs_DB != num_POs ) { printf("ERROR: Number of PIs %d or POs %d in database do NOT match those in common.h %d and %d!\n", num_PIs_DB, num_POs_DB, num_PIs, num_POs); exit(EXIT_FAILURE); } // Trust protocol rc = sqlite3_open(":memory:", &DB_Trust_AT); if ( rc != 0 ) { printf("Failed to open Trust_AT Database: %s\n", sqlite3_errmsg(DB_Trust_AT)); sqlite3_close(DB_Trust_AT); exit(EXIT_FAILURE); } printf("Reading filesystem database '%s' into memory!\n", DB_name_Trust_AT); fflush(stdout); if ( LoadOrSaveDb(DB_Trust_AT, DB_name_Trust_AT, 0) != 0 ) { printf("Failed to open and copy into memory '%s': ERR: %s\n", DB_name_Trust_AT, sqlite3_errmsg(DB_Trust_AT)); sqlite3_close(DB_Trust_AT); exit(EXIT_FAILURE); } // PUF-Cash V3.0 protocol DATABASE STUFF rc = sqlite3_open(":memory:", &DB_PUFCash_V3); if ( rc != 0 ) { printf("Failed to open PUFCash_V3 Database: %s\n", sqlite3_errmsg(DB_PUFCash_V3)); sqlite3_close(DB_PUFCash_V3); exit(EXIT_FAILURE); } printf("Reading filesystem database '%s' into memory!\n", DB_name_PUFCash_V3); fflush(stdout); if ( LoadOrSaveDb(DB_PUFCash_V3, DB_name_PUFCash_V3, 0) != 0 ) { printf("Failed to open and copy into memory '%s': ERR: %s\n", DB_name_PUFCash_V3, sqlite3_errmsg(DB_PUFCash_V3)); sqlite3_close(DB_PUFCash_V3); exit(EXIT_FAILURE); } // ========================= // Set some of the params in the data structure. SHP.CtrlRegA = CtrlRegA; SHP.DataRegA = DataRegA; SHP.ctrl_mask = ctrl_mask; // 10_11_2022: For testing COBRA and RangeConstant -- added this field. Can be used in other places for PUF-Cash too. StringCreateAndCopy(&(SHP.My_IP), My_IP); // After device authenticates successfully with IA, IA sends its ID from the NAT database to the device. The device will use this as it's ID. SHP.chip_num = -1; // 7_2_2022: Adding this for the PUF-Cash V3.0 AliceWithdrawal process. This is also filled in by GenLLK(). THIS CAN BE DONE // during device provisioning where the challenge are drawn from the ANONYMOUS DB, or by doing an anonymous authentication // at any time with the server. SHP.anon_chip_num = -1; SHP.DB_Challenges = DB_Challenges; SHP.DB_name_Challenges = DB_name_Challenges; SHP.use_database_chlngs = use_database_chlngs; SHP.DB_design_index = design_index; SHP.DB_ChallengeSetName = ChallengeSetName; SHP.DB_ChallengeGen_seed = ChallengeGen_seed; SHP.DB_Trust_AT = DB_Trust_AT; SHP.DB_name_Trust_AT = DB_name_Trust_AT; SHP.DB_PUFCash_V3 = DB_PUFCash_V3; SHP.DB_name_PUFCash_V3 = DB_name_PUFCash_V3; SHP.eCt_num_bytes = ECT_NUM_BYTES; // Alice's withdrawal amount SHP.Alice_EWA = NULL; SHP.Alice_K_AT = NULL; // Other protocol. This must also match the length of KEK_TARGET_NUM_KEY_BITS/8. Might make more sense to just set it to that even // though we use KEK session key generation to generate the MAT_LLK. // SHP.MAT_LLK_num_bytes = SE_TARGET_NUM_KEY_BITS/8; // Other protocol. This must also match the length of KEK_TARGET_NUM_KEY_BITS/8. Might make more sense to just set it to that even // though we use KEK session key generation to generate the PHK_A_nonce. // SHP.PHK_A_num_bytes = SE_TARGET_NUM_KEY_BITS/8; SHP.ZHK_A_num_bytes = SE_TARGET_NUM_KEY_BITS/8; // For GenLLK() SHP.KEK_LLK_num_bytes = KEK_TARGET_NUM_KEY_BITS/8; // For POP SHP.POP_LLK_num_bytes = KEK_TARGET_NUM_KEY_BITS/8; // These we will eventually come from the verifier via a message. SHP.num_PIs = num_PIs; SHP.num_POs = num_POs; SHP.fix_params = fix_params; SHP.num_required_PNDiffs = NUM_REQUIRED_PNDIFFS; SHP.num_SF_bytes = NUM_REQUIRED_PNDIFFS * SF_WORDS_TO_BYTES_MULT; SHP.num_SF_words = NUM_REQUIRED_PNDIFFS; // 1_1_2022: If TRIMCODE_CONSTANT is <= 32, then we can preserve on precision bit in the iSpreadFactors for the device, else we cannot preserve any. if ( TRIMCODE_CONSTANT <= 32 ) SHP.iSpreadFactorScaler = 2; else SHP.iSpreadFactorScaler = 1; if ( (SHP.iSpreadFactors = (signed char *)calloc(SHP.num_SF_words, sizeof(signed char))) == NULL ) { printf("ERROR: Failed to allocate storage for iSpreadFactors!\n"); exit(EXIT_FAILURE); } if ( (SHP.verifier_SHD = (unsigned char *)calloc(SHP.num_required_PNDiffs/8, sizeof(unsigned char))) == NULL ) { printf("ERROR: Failed to allocate storage for verifier_SHD!\n"); exit(EXIT_FAILURE); } if ( (SHP.verifier_SBS = (unsigned char *)calloc(SHP.num_required_PNDiffs/8, sizeof(unsigned char))) == NULL ) { printf("ERROR: Failed to allocate storage for verifier_SBS!\n"); exit(EXIT_FAILURE); } if ( (SHP.device_SHD = (unsigned char *)calloc(SHP.num_required_PNDiffs/8, sizeof(unsigned char))) == NULL ) { printf("ERROR: Failed to allocate storage for device_SHD!\n"); exit(EXIT_FAILURE); } if ( (SHP.device_SBS = (unsigned char *)calloc(SHP.num_required_PNDiffs/8, sizeof(unsigned char))) == NULL ) { printf("ERROR: Failed to allocate storage for device_SBS!\n"); exit(EXIT_FAILURE); } SHP.verifier_SHD_num_bytes = 0; SHP.verifier_SBS_num_bytes = 0; SHP.device_SHD_num_bytes = 0; SHP.device_SBS_num_bits = 0; // Note: MAX_GENERATED_NONCE_BYTES MUST BE LARGER THAN NUM_XOR_NONCE_BYTES. SHP.nonce_base_address = nonce_base_address; SHP.max_generated_nonce_bytes = MAX_GENERATED_NONCE_BYTES; SHP.num_required_nonce_bytes = NUM_XOR_NONCE_BYTES; // This is filled in by CollectPNs as the hardware reads nonce bytes. SHP.num_device_n1_nonces = 0; if ( (SHP.device_n1 = (unsigned char *)calloc(SHP.max_generated_nonce_bytes, sizeof(unsigned char))) == NULL ) { printf("ERROR: Failed to allocate storage for verifier_n2!\n"); exit(EXIT_FAILURE); } if ( (SHP.verifier_n2 = (unsigned char *)calloc(SHP.num_required_nonce_bytes, sizeof(unsigned char))) == NULL ) { printf("ERROR: Failed to allocate storage for verifier_n2!\n"); exit(EXIT_FAILURE); } if ( (SHP.XOR_nonce = (unsigned char *)calloc(SHP.num_required_nonce_bytes, sizeof(unsigned char))) == NULL ) { printf("ERROR: Failed to allocate storage for XOR_nonce!\n"); exit(EXIT_FAILURE); } SHP.vec_chunk_size = CHLNG_CHUNK_SIZE; SHP.XMR_val = XMR_VAL; memcpy((char *)SHP.AES_IV, (char *)AES_IV, AES_IV_NUM_BYTES); SHP.SE_target_num_key_bits = SE_TARGET_NUM_KEY_BITS; SHP.SE_final_key = NULL; SHP.authen_min_bitstring_size = AUTHEN_MIN_BITSTRING_SIZE; // KEK information presumably stored in NVM for regeneration, preserved here in separate fields. SHP.KEK_target_num_key_bits = KEK_TARGET_NUM_KEY_BITS; SHP.KEK_final_enroll_key = NULL; SHP.KEK_final_regen_key = NULL; SHP.KEK_final_XMR_SHD = NULL; // 5_11_2021: For tracking the number of minority bit flips with KEK FSB mode (NOT NE mode). SHP.KEK_BS_regen_arr = NULL; SHP.KEK_final_SpreadFactors_enroll = NULL; SHP.KEK_num_vecs = 0; SHP.KEK_num_rise_vecs = 0;; SHP.KEK_has_masks = 1; SHP.KEK_first_vecs_b = NULL; SHP.KEK_second_vecs_b = NULL; SHP.KEK_masks_b = NULL; if ( (SHP.KEK_XOR_nonce = (unsigned char *)calloc(SHP.num_required_nonce_bytes, sizeof(unsigned char))) == NULL ) { printf("ERROR: Failed to allocate storage for XOR_nonce!\n"); exit(EXIT_FAILURE); } SHP.num_direction_chlng_bits = NUM_DIRECTION_CHLNG_BITS; // For Special KEK mode data from hardware. Will eventually be eliminated once I change the VHDL to do this in hardware. SHP.KEK_num_iterations = 0; // Allocate space for the authentication nonce received from server during device authentication or generated locally // for transmission to server for server authentication. if ( (SHP.KEK_authentication_nonce = (unsigned char *)calloc(num_KEK_authen_nonce_bytes, sizeof(unsigned char))) == NULL ) { printf("ERROR: Failed to allocate storage for KEK_authentication_nonce!\n"); exit(EXIT_FAILURE); } SHP.num_KEK_authen_nonce_bits = num_KEK_authen_nonce_bytes*8; SHP.num_KEK_authen_nonce_bits_remaining = SHP.num_KEK_authen_nonce_bits; SHP.DA_cobra_key = NULL; // XMR_SHD that is generated during KEK_DeviceAuthentication during each iteration (to be concatenated to a larger blob and // sent to server). if ( (SHP.KEK_authen_XMR_SHD_chunk = (unsigned char *)calloc(SHP.num_required_PNDiffs/8, sizeof(unsigned char))) == NULL ) { printf("ERROR: Failed to allocate storage for KEK_authen_XMR_SHD_chunk!\n"); exit(EXIT_FAILURE); } SHP.num_vecs = 0; SHP.num_rise_vecs = 0;; SHP.has_masks = 1; SHP.first_vecs_b = NULL; SHP.second_vecs_b = NULL; SHP.masks_b = NULL; // SHP.PeerTrust_LLK = NULL; SHP.ZeroTrust_LLK = NULL; SHP.param_LFSR_seed_low = 0; SHP.param_LFSR_seed_high = 0; SHP.param_RangeConstant = RANGE_CONSTANT; SHP.param_SpreadConstant = SPREAD_CONSTANT; SHP.param_Threshold = THRESHOLD_CONSTANT; SHP.param_TrimCodeConstant = TRIMCODE_CONSTANT; SHP.param_PCR_or_PBD_or_PO = PCR_or_PBD_or_PO; // 10_28_2022: Get the personalized ScalingConstant from the command line. NOTE: This is passed into the state machine as a FIXED POINT value // with SCALING_PRECISION_NB bits of precision (currently 11 bits), xxxxx.xxxxxxxxxxx. Convert from floating point to scaled integer. So a // scaling value of 1.0 will be equal to 1 << SCALING_PRECISION_NB, which is 2^11 = 2048 (0000100000000000). NOTE: MyScalingConstant VALUE MUST BE POSITIVE and // between 1.0 and x.0 (current 5.0) above. So values here are between 4096 and 20480. // 11_12_2022: Adding this 'do_scaling' flag, and initializing it to 0. COBRA and possibly SKE (PARCE) are the only functions that set it to 1. SHP.do_scaling = 0; SHP.MyScalingConstant = (int)(command_line_SC * pow(2.0, (float)SCALING_PRECISION_NB)); // Sanity check if ( SHP.MyScalingConstant < 0 || SHP.MyScalingConstant > (MAX_SCALING_VALUE << SCALING_PRECISION_NB) ) { printf("ERROR: MyScalingConstant MUST be >= 0 and <= %d\n", MAX_SCALING_VALUE << SCALING_PRECISION_NB); exit(EXIT_FAILURE); } #ifdef DEBUG if ( SHP.MyScalingConstant == (1 << SCALING_PRECISION_NB) ) { printf("NO SCALING WILL OCCUR: ScalingConstant IS 1.0\n"); fflush(stdout); } else { printf("ScalingConstant: %f\tScaled FixedPoint %d\n", command_line_SC, SHP.MyScalingConstant); fflush(stdout); } #endif // The PL-side TRNG_LFSR is 64 bits. Note that we currently only suport loading the low-order 16-bits of the seed register. SHP.TRNG_LFSR_seed = TRNG_LFSR_seed; // For frequency statistics of the TRNG. Need to declare these here for the TTP -- can NOT make them static in multi-threaded apps. SHP.num_ones = 0; SHP.total_bits = 0; SHP.iteration = 0; // 10_31_2021: We are now using a seed to specify the vector sequence on the device, TTP and verifier. When challenges are selected, we depend // on the sequence returned by rand() to be the same no matter where this routine runs, device, TT or verifier. The verifier and TTP are multi-threaded // and therefore it is possible that multiple threads call this routine simultaneously, interrupting the sequence generated by rand() (rand is NOT re-entrant). // If this occurs, then the vector challenges used by the, e.g., verifier and device will be different and the security function will fail. When the device // calls this function, the mutex is NULL. SHP.GenChallenge_mutex_ptr = NULL; SHP.do_COBRA = DO_COBRA; SHP.DUMP_BITSTRINGS = DUMP_BITSTRINGS; SHP.DEBUG_FLAG = DEBUG_FLAG; // ================================================================================================ // Generate an LLK with the Bank for PeerTrust. This is the LLK that we will use as input to the // SHA-3 hash (plus a nonce) to generate ZHK_A_nonce (keyed-hash of KK_A -- which is the KEK key // generated in FSB mode). Also need a 'HOSE' LLK (hardware-oriented secure enclave) for Propagation // of Providence and PUF-Cash. // Generate the KEK long-lived KEK, either by running KEK_Enroll by communicating with IA to get challenges // (if no Chlng info exists in the AuthenticationToken.db), or by reading out the LLK Chlng information // and running KEK_Regen. Called by both the device and TTP. // Setting allow_multiple_LLK to 1 allows MORE THAN one LLK_type = 2 Chlngs to exist. There should be // only one although I have integrated a method to associate a Chlng number with the PeerTrust_LLKs, // so in the future, we can allow more than one. // // NOTE: GenLLK generates the PeerTrust_LLK and where we obtain Alice's unique id (chip_num) from the // NON-anonymous DB. I added an ID transfer from IA in DA_Report, which is called as part of device // authentication, i.e., KEK_DeviceAuthentication_SKE. The SHP.chip_num (SHP_ptr->chip_num) field is filled in. // The LLK_type param here indicates whether we are saving the PeerTrust LLK (2) int allow_multiple_LLK = 0; int open_socket = 1; int LLK_type = 2; int zero_trust_LLK_index; zero_trust_LLK_index = GenLLK(MAX_STRING_LEN, &SHP, open_socket, Bank_IP, port_number, Bank_socket_desc, allow_multiple_LLK, LLK_type, SHP.KEK_LLK_num_bytes); // Save the new chip num to the anon_chip_num field and restore the non-anonymous to the chip_num field. SHP.anon_chip_num = SHP.chip_num; printf("MyChip NON-ANONYMOUS server ID %d\n", SHP.chip_num); fflush(stdout); #ifdef DEBUG #endif // If NO AT elements exist in Alice's ZeroTrust table, then carry out enrollment. This is NOT necessary when we NEED to do // enrollment. Only doing this to prevent ZeroTrust_Enroll from being called over and over again during testing. // Check if ATs exist. This flag dominates. Only the number of NOT USED ATs are returned if set int report_tot_num_ATs_only = 1; // These flags irrelevant here. int get_only_customer_AT = 0; int return_customer_AT_info = 0; int customer_chip_num = -1; // NOTE: We MUST DO the authentication here for a customer. int *chip_num_arr = NULL; int *chlng_num_arr = NULL; unsigned char **ZHK_A_nonce_arr = NULL; unsigned char **nonce_arr = NULL; int unused; int is_TTP = 0; Bank_socket_desc = 0; unsigned char *session_key = NULL; if ( ZeroTrustGetCustomerATs(MAX_STRING_LEN, SHP.DB_Trust_AT, &chip_num_arr, &chlng_num_arr, SHP.ZHK_A_num_bytes, &ZHK_A_nonce_arr, &nonce_arr, get_only_customer_AT, customer_chip_num, return_customer_AT_info, report_tot_num_ATs_only, &unused) == 0 ) ZeroTrust_Enroll(MAX_STRING_LEN, &SHP, Bank_IP, port_number, zero_trust_LLK_index, is_TTP, Bank_socket_desc, session_key); // ************************* DONE ANONYMOUSLY BELOW ************************* // ***** For PUFCash V3.0. // TO_DO: // ALICE ONLY NEEDS TO GET HER anon_chip_num here, so we need an anonymous authentication with the Bank. // Possible, just haven't coded it here yet. Making her anon_chip_num the same as her non_anon_chip_num // for now. // DO GetPOPLLKs AS ONE OPERATION WITH THE anonymous authentication operation. // POP and ZeroTRust. Get LLKs from Bank and populate the POP DB. NOTE: Bank sends all LLKs except for // the one belonging to Alice (this device). We just got our unique 'anon_chip_num' from the GenLLK above. // GetPOPLLKs(MAX_STRING_LEN, &SHP, Bank_IP, port_number); // ************************* DONE ANONYMOUSLY ABOVE ************************* printf("GenLLK(): MyChip ANONYMOUS server ID %d\n", SHP.anon_chip_num); fflush(stdout); #ifdef DEBUG #endif // The Challenges DB is read-only. sqlite3_close(DB_Challenges); printf("Saving 'in memory' '%s' to filesystem!\n", SHP.DB_name_Trust_AT); fflush(stdout); if ( LoadOrSaveDb(SHP.DB_Trust_AT, SHP.DB_name_Trust_AT, 1) != 0 ) { printf("Failed to store 'in memory' database to %s: %s\n", SHP.DB_name_Trust_AT, sqlite3_errmsg(SHP.DB_Trust_AT)); sqlite3_close(SHP.DB_Trust_AT); exit(EXIT_FAILURE); } sqlite3_close(SHP.DB_Trust_AT); printf("Saving 'in memory' '%s' to filesystem!\n", SHP.DB_name_PUFCash_V3); fflush(stdout); if ( LoadOrSaveDb(SHP.DB_PUFCash_V3, SHP.DB_name_PUFCash_V3, 1) != 0 ) { printf("Failed to store 'in memory' database to %s: %s\n", SHP.DB_name_PUFCash_V3, sqlite3_errmsg(SHP.DB_PUFCash_V3)); sqlite3_close(SHP.DB_PUFCash_V3); exit(EXIT_FAILURE); } sqlite3_close(SHP.DB_PUFCash_V3); fflush(stdout); system("sync"); return 0; }