#include #include #include #include #include #include #include #include #include #include #include //inet_addr #include //hostent /******************************** DEFINES ***********************************/ #define OUT_CP_RESET 31 #define OUT_CP_LCTD_START 30 #define OUT_CP_FIRST_HASH 23 #define OUT_CP_START_HASH 22 #define OUT_CP_MODE_FUNC_PUF 21 #define OUT_CP_DTO_DATA_READY 20 #define OUT_CP_DTO_RESTART 19 #define OUT_CP_DTO_VEC_NUM 18 #define OUT_CP_DTI_DONE_READING 17 #define OUT_CP_DTI_RESTART 16 #define IN_SM_READY 31 #define IN_SM_DTI_DATA_READY 30 #define IN_SM_DTO_DONE_READING 29 // GPIO 0 #define GPIO_0_BASE_ADDR 0x41200000 #define CTRL_DIRECTION_MASK 0x00 #define DATA_DIRECTION_MASK 0xFFFFFFFF // **************************** LABVIEW ********************************** // CoreSM is the Agilent B2910A #define InstrCoreSM "CoreSrcMeter" #define InstrTC "TempChamber" #define CommandPwrUp "PUP" #define CommandPwrDwn "PWN" #define CommandReadCurrentAmplitude "ReadCurrConfigAmpl" #define CommandSetCurrentAmplitude "SetCurrConfigAmpl" #define CommandReadTemperature "ReadTemperature" #define CommandSetTemperature "SetTemperature" /****************************************************************************/ /********************************* GetAck ***********************************/ /****************************************************************************/ // Simply gets the acknowledgement string from labview that the operation was // carried out. void GetAck() { char ACK_str[50]; scanf("%s", ACK_str); if ( strcmp(ACK_str, "ACK") != 0 ) { printf("ERROR: GetAck(): Expected ACK, received %s\n", ACK_str); fflush(stdout); exit(EXIT_FAILURE); } return; } /****************************************************************************/ /****************************** SetTVCorner *********************************/ /****************************************************************************/ // Issue labview commands to set the TV corner. void SetTVCorner(int str_length, int TV_num, int max_TVs, int temperatures[max_TVs], float currents_uAs[max_TVs], int socket_desc) { float present_curr_src, present_temperature; int iterations; // char num_str[str_length]; // char temp_str[str_length]; // Power up the current source. Don't do this -- it should already be on and ready. // Powering it up is likely to reset it. // printf("%s %s\n", InstrCoreSM, CommandPwrUp); GetAck(); // usleep(1000000); // Set voltage. The Agilent power supply is used to change voltage on the Zedboard. It is // configured as a current source and connected to the Vfb pin on the MAXIM part (see README.jim). // It is important to change the voltage SLOWLY by changing the current source value 1 uA // at a time and then waiting. This will prevent the Zedboard from resetting (we hope). Note // that the Zedboard is spec'ed to +/- 5% only. Get the current value of the current source. printf("%s %s\n", InstrCoreSM, CommandReadCurrentAmplitude); scanf("%f", &present_curr_src); // No more than 21 iterations are ever required. Never allow the value to get outside of -60e-6 to -30e-6 for ( iterations = 0; iterations < 21 && present_curr_src != currents_uAs[TV_num] && present_curr_src > -60e-6 && present_curr_src < -30e-6; iterations++ ) { if ( present_curr_src < currents_uAs[TV_num] ) { printf("%s %s %f\n", InstrCoreSM, CommandSetCurrentAmplitude, present_curr_src + 1e-6); GetAck(); } else { printf("%s %s %f\n", InstrCoreSM, CommandSetCurrentAmplitude, present_curr_src - 1e-6); GetAck(); } usleep(500000); printf("%s %s\n", InstrCoreSM, CommandReadCurrentAmplitude); scanf("%f", &present_curr_src); usleep(500000); } printf("%s %s\n", InstrCoreSM, CommandReadCurrentAmplitude); scanf("%f", &present_curr_src); usleep(500000); // Set the temperature (if necessary). printf("%s %s\n", InstrTC, CommandReadTemperature); scanf("%f", &present_temperature); // Wake-up the temperature chamber to keep it at 25C. Not going to work... // printf("%s %s %d\n", InstrTC, CommandSetTemperature, temperatures[TV_num] - 1); // scanf("%f", &present_temperature); printf("%s %s %d\n", InstrTC, CommandSetTemperature, temperatures[TV_num]); scanf("%f", &present_temperature); return; } /****************************************************************************/ /*************************** OpenSocketServer *******************************/ /****************************************************************************/ // For enrollment, the token is the 'server' while the verifier is the 'client' // Serve-up a socket connection for clients void OpenSocketServer(int str_length, int *server_socket_desc_ptr, char server_IP[str_length], int port_number, int *client_socket_desc_ptr, struct sockaddr_in *client_addr_ptr, int accept_only) { struct sockaddr_in server_addr; int sizeof_sock; int queue_size = 20; // Create a socket and prepare the socket_in structure. if ( accept_only == 0 ) { printf("OpenSocketServer(): Creating a socket\n"); fflush(stdout); *server_socket_desc_ptr = socket(AF_INET, SOCK_STREAM, 0); if ( *server_socket_desc_ptr == -1 ) { printf("ERROR: OpenSocketServer(): Could not create socket"); fflush(stdout); } // Prepare the sockaddr_in structure for the server server_addr.sin_family = AF_INET; server_addr.sin_addr.s_addr = inet_addr(server_IP); server_addr.sin_port = htons(port_number); memset(server_addr.sin_zero, 0, 8); // Bind the server with the socket. if ( bind(*server_socket_desc_ptr, (struct sockaddr *) &server_addr, sizeof(server_addr)) < 0 ) { printf("ERROR: OpenSocketServer(): Failed to bind!\n"); fflush(stdout); exit(EXIT_FAILURE); } // printf("OpenSocketServer(): Bind done!\n"); fflush(stdout); // Listen to the socket. 'queue_size' indicates how many requests can be pending before an error is // returned to the remote computer requesting a connection. listen(*server_socket_desc_ptr, queue_size); } printf("OpenSocketServer(): Waiting for incoming connections from clients ....\n"); fflush(stdout); // Waiting and accept incoming connection from the client. Token address information is returned // Note: you can also use 'select' here to determine if a connection request exists. sizeof_sock = sizeof(struct sockaddr_in); while ( (*client_socket_desc_ptr = accept(*server_socket_desc_ptr, (struct sockaddr *)client_addr_ptr, (socklen_t*)&sizeof_sock)) < 0 ) ; // if (*client_socket_desc_ptr < 0) // { printf("ERROR: OpenSocketServer(): Failed accept\n"); fflush(stdout); exit(EXIT_FAILURE); } printf("OpenSocketServer(): Connection accepted!\n"); fflush(stdout); } /****************************************************************************/ /******************************** SockGetB **********************************/ /****************************************************************************/ // This function is designed to buffer data but in kernel space. It allows // binary data to be transmitted. To accomplish this, the first two bytes are // interpreted as the length of the binary byte stream that follows. int SockGetB(unsigned char *buffer, int buffer_size, int socket_desc) { int tot_bytes_received, target_num_bytes; char buffer_num_bytes[3]; // Call until two bytes are returned. The 2-byte buffer represents a number that is to be interpreted as the exact // number of binary bytes that will follow in the socket. target_num_bytes = 2; tot_bytes_received = 0; while ( tot_bytes_received < target_num_bytes ) if ( (tot_bytes_received += recv(socket_desc, &buffer_num_bytes[tot_bytes_received], target_num_bytes - tot_bytes_received, 0)) < 0 ) { printf("ERROR: SockGetB(): Error in receiving two byte cnt!\n"); fflush(stdout); return -1; } // Translate the two binary bytes into an integer. target_num_bytes = (int)(buffer_num_bytes[1] << 8) + (int)buffer_num_bytes[0]; // DEBUG //printf("SockGetB(): fetching %d bytes from socket\n", target_num_bytes); fflush(stdout); // Sanity check. if ( target_num_bytes > buffer_size ) { printf("ERROR: SockGetB(): 'target_num_bytes' %d is larger than buffer input size %d\n", target_num_bytes, buffer_size); fflush(stdout); return -1; } // Now start reading binary bytes from the socket tot_bytes_received = 0; while ( tot_bytes_received < target_num_bytes ) if ( (tot_bytes_received += recv(socket_desc, &buffer[tot_bytes_received], target_num_bytes - tot_bytes_received, 0)) < 0 ) { printf("ERROR: SockGetB(): Error in receiving transmitted data!\n"); fflush(stdout); return -1; } // Sanity check if ( tot_bytes_received != target_num_bytes ) { printf("ERROR: SockGetB(): Read more bytes then requested -- 'tot_bytes_received' %d is larger than target %d\n", tot_bytes_received, target_num_bytes); fflush(stdout); return -1; } // DEBUG //printf("SockGetB(): received %d bytes\n", tot_bytes_received); fflush(stdout); return tot_bytes_received; } /****************************************************************************/ /******************************* SockSendB **********************************/ /****************************************************************************/ // This function sends binary or ASCII data of 'buffer_size' unsigned characters // through the socket. It first sends two binary bytes that represent the length // of the binary or ASCII byte stream that follows. int SockSendB(unsigned char *buffer, int buffer_size, int socket_desc) { unsigned char num_bytes[2]; // Sanity check. Don't yet support transfers larger than 65,536 bytes. if ( buffer_size > 65535 ) { printf("ERROR: SockSendB(): Size of buffer %d larger than max (65536)!\n", buffer_size); fflush(stdout); return -1; } num_bytes[1] = (unsigned char)((buffer_size & 0x0000FF00) >> 8); num_bytes[0] = (unsigned char)(buffer_size & 0x000000FF); if ( send(socket_desc, num_bytes, 2, 0) < 0 ) { printf("ERROR: SockSendB(): Send 'num_bytes' %d failed\n", buffer_size); fflush(stdout); return -1; } if ( send(socket_desc, buffer, buffer_size, 0) < 0 ) { printf("ERROR: SockSendB(): Send failed\n"); fflush(stdout); return -1; } return 0; } /****************************************************************************/ /************************* ReceiveEnrollVectors *****************************/ /****************************************************************************/ // Get all vectors to be applied during the secure enrollment process int ReceiveEnrollVectors(int str_length, int verifier_socket_desc, int max_vecs, unsigned char *first_vecs_b[max_vecs], unsigned char *second_vecs_b[max_vecs], int vec_len_bits, int *num_rise_vecs_ptr) { int num_vecs, vec_pair_num, vec_num; char num_vecs_str[str_length]; unsigned char *vec_ptr; // Get the number of vectors that verifier intends to send. if ( SockGetB((unsigned char *)num_vecs_str, str_length, verifier_socket_desc) < 0 ) { printf("ERROR: ReceiveEnrollVectors(): Failed to receive 'num_vecs_str'!\n"); fflush(stdout); exit(EXIT_FAILURE); } // DEBUG //printf("ReceiveEnrollVectors(): 'num_vecs_str' received from verifier '%s'\n", num_vecs_str); fflush(stdout); if ( sscanf(num_vecs_str, "%d %d", &num_vecs, num_rise_vecs_ptr) != 2 ) { printf("ERROR: ReceiveEnrollVectors(): Expected 'num_vecs' and 'num_rise_vecs' in '%s'\n", num_vecs_str); fflush(stdout); exit(EXIT_FAILURE); } // Receive the first_vecs and second_vecs sent from the verifier pair by pair; vec_num = 0; vec_pair_num = 0; while ( vec_num != num_vecs ) { // Allocate space to store the binary vectors if ( vec_pair_num == 0 ) { first_vecs_b[vec_num] = (unsigned char *)malloc(sizeof(char)*vec_len_bits/8); vec_ptr = first_vecs_b[vec_num]; } else { second_vecs_b[vec_num] = (unsigned char *)malloc(sizeof(char)*vec_len_bits/8); vec_ptr = second_vecs_b[vec_num]; } // Get the binary vector data if ( SockGetB(vec_ptr, vec_len_bits/8, verifier_socket_desc) != vec_len_bits/8 ) { printf("ERROR: ReceiveEnrollVectors(): number of vector bytes received is not equal to %d\n", vec_len_bits/8); fflush(stdout); exit(EXIT_FAILURE); } // DEBUG //printf("ReceiveEnrollVectors(): Vector %d\n\t", vec_num); //int i; //for ( i = 0; i < vec_len_bits/8; i++ ) // printf("%02X ", vec_ptr[i]); //printf("\n"); // Increment to next vector after both vectors (first and second) have been received. if ( vec_pair_num == 1 ) vec_num++; vec_pair_num = !vec_pair_num; } // DEBUG printf("ReceiveEnrollVectors(): %d vector pairs received from verifier!\n", vec_num); fflush(stdout); return num_vecs; } /****************************************************************************/ /****************************** LoadVecPair *********************************/ /****************************************************************************/ // Transfer a vector pair through the GPIO to the VHDL side. void LoadVecPair(int str_length, volatile unsigned int *CtrlRegA, volatile unsigned int *DataRegA, unsigned char *first_vec_b, unsigned char *second_vec_b, int ctrl_mask, int vec_len_bits, int vec_chunk_size) { int vec_pair_num, word_num; unsigned char *vec_ptr; int vec_val_chunk; // Sanity check if ( (vec_len_bits % vec_chunk_size) != 0 ) { printf("ERROR: LoadVecPair(): Vector size %d must be evenly divisible by %d!\n", vec_len_bits, vec_chunk_size); fflush(stdout); exit(EXIT_FAILURE); } // Reset the VHDL pointers to the vector buffers. *CtrlRegA = ctrl_mask | (1 << OUT_CP_DTO_RESTART); *CtrlRegA = ctrl_mask; //printf("LoadVecPair(): Reset VHDL vector pointers!\n"); fflush(stdout); // Send the 2 416-bit binary vectors, 16-bits at a time, starting with the low order to high order bits. // Vector format is RndI, RndC, RndI_n, RndC_n from high to low. for ( vec_pair_num = 0; vec_pair_num < 2; vec_pair_num++ ) { // Update the ctrl_mask with the vector num on second iteration. if ( vec_pair_num == 1 ) ctrl_mask = ctrl_mask | (1 << OUT_CP_DTO_VEC_NUM); //printf(stdout, "LoadVecPair(): Current control mask '%08X'\n", ctrl_mask); fflush(stdout); // Iterate over each 26 16-bit chunks of string data. Verifier has ordered the vector data from low order // to high order, i.e., the exact format that we need to load it up by in the VHDL. for ( word_num = 0; word_num < vec_len_bits/vec_chunk_size; word_num++ ) { // Add 2 bytes at a time to the pointer. In binary, there are only 52 bytes (in ASCII, there were 416 bytes). if ( vec_pair_num == 0 ) vec_ptr = first_vec_b + word_num*2; else vec_ptr = second_vec_b + word_num*2; //printf("LoadVecPair(): vec_ptr pointer '%s'!\n", vec_ptr); fflush(stdout); vec_val_chunk = (vec_ptr[1] << 8) + vec_ptr[0]; //printf("LoadVecPair(): 16-bit binary value in hex '%04X'\n", vec_val_chunk); fflush(stdout); // Four step protocol // 1) Assert 'data_ready' while putting the 16-bit binary value on the low order bits of CtrlReg //printf("LoadVecPair(): Writing 'data_ready' with 16-bit binary value in hex '%04X'\n", vec_val_chunk); fflush(stdout); *CtrlRegA = ctrl_mask | (1 << OUT_CP_DTO_DATA_READY) | vec_val_chunk; // 2) Wait for 'done_reading to go to 1 (it is low by default). State machine latches data in 2 clk cycles. // Maintain 1 on 'data_ready' and continue to hold 16-bit binary chunk. //printf("LoadVecPair(): Waiting state machine 'done_reading' to be set to '1'\n"); fflush(stdout); while ( (*DataRegA & (1 << IN_SM_DTO_DONE_READING)) == 0 ); // 3) Once 'done_reading' goes to 1, set 'data_ready' to 0 and remove chunk; //printf("LoadVecPair(): De-asserting 'data_ready'\n"); fflush(stdout); *CtrlRegA = ctrl_mask; // 4) Wait for 'done_reading to go to 0. //printf("LoadVecPair(): Waiting state machine 'done_reading' to be set to '0'\n"); fflush(stdout); while ( (*DataRegA & (1 << IN_SM_DTO_DONE_READING)) != 0 ); //printf("LoadVecPair(): Done handshake associated with vector chunk transfer\n"); fflush(stdout); } } return; } /****************************************************************************/ /**************************** GenGetTimingVals ******************************/ /****************************************************************************/ // Start the PUF engine with the current vector to get the timing values. Once // it finishes, fetch each of the timing values. int GenGetTimingVals(volatile unsigned int *CtrlRegA, volatile unsigned int *DataRegA, int max_outputs, int max_sams, short timing_val_arr[max_outputs][max_sams], short output_pos[max_outputs][max_sams], int sam_num, int ctrl_mask) { int output_num, num_non_zero_timing_vals; int timing_val; //printf("GenGetTimingVals(): BEGIN\n"); fflush(stdout); // Sanity check. PUF engine MUST be 'ready' if ( (*DataRegA & (1 << IN_SM_READY)) == 0 ) { printf("ERROR: GenGetTimingVals(): PUF Engine is NOT ready!\n"); fflush(stdout); exit(EXIT_FAILURE); } //printf("GenGetTimingVals(): PUF/hash engine is ready -- starting PUF engine!\n"); fflush(stdout); // Start the PUF engine. Vector pair has already been loaded. *CtrlRegA = ctrl_mask | (1 << OUT_CP_LCTD_START); *CtrlRegA = ctrl_mask; //printf("GenGetTimingVals(): Waiting PUF engine to finish!\n"); fflush(stdout); // Wait for the PUF engine to finish generating the timing data. while ( (*DataRegA & (1 << IN_SM_READY)) == 0 ); //printf("GenGetTimingVals(): PUF engine DONE!\n"); fflush(stdout); // Reset the timing pointers in the VHDL code. *CtrlRegA = ctrl_mask | (1 << OUT_CP_DTI_RESTART); *CtrlRegA = ctrl_mask; //printf("GenGetTimingVals(): Checking 'data_ready' of DataTransferOut!\n"); fflush(stdout); // Wait for 'data_ready' to become 1 after the pointer reset (should already be 1). while ( (*DataRegA & (1 << IN_SM_DTI_DATA_READY)) == 0 ); //printf("GenGetTimingVals(): 'data_ready' is set!\n"); fflush(stdout); // ================== // Get timing values num_non_zero_timing_vals = 0; for ( output_num = 0; output_num < max_outputs; output_num++ ) { // Read a timing value -- current version uses only 10 bits (for timing values between 0 and 1023), even though phase // shift can increase to 1120. NEED TO KEEP AN EYE ON THIS!!!!!!!!!!!!!!!!!!!!!!!! Save 400 FFs in the implementation // right now. Once we move to SRAM, increase this to 11 bits! timing_val = *DataRegA & 0x7FF; // If timing value is 0, then the path has NO transition. if ( timing_val > 0 ) { // Don't really need to save these since we are writing them to a file directly above. timing_val_arr[num_non_zero_timing_vals][sam_num] = timing_val; output_pos[num_non_zero_timing_vals][sam_num] = output_num; num_non_zero_timing_vals++; } // Four phases here. // 1) Got timing value above, indicate we are done reading. *CtrlRegA = ctrl_mask | (1 << OUT_CP_DTI_DONE_READING); // 2) Wait for 'data_ready' to become 0. while ( (*DataRegA & (1 << IN_SM_DTI_DATA_READY)) != 0 ); // 3) Reset done_reading to 0 *CtrlRegA = ctrl_mask; // 4) Wait for 'data_ready' to become 1. while ( (*DataRegA & (1 << IN_SM_DTI_DATA_READY)) == 0 ); } return num_non_zero_timing_vals; } /****************************************************************************/ /************************* LoadHashInputBinStr ******************************/ /****************************************************************************/ // Load the 'hash_in' input parameter into the VHDL registers by transferring across // the GPIO interface. void LoadHashInputBinStr(int str_length, volatile unsigned int *CtrlRegA, volatile unsigned int *DataRegA, char *hash_in_str, int ctrl_mask, int hash_in_len_bits, int hash_in_chunk_size) { int hash_in_val; char *char_pos; int i, j, k; // Sanity check -- the length of the hash must be divisible by 8 if ( (hash_in_len_bits % (hash_in_chunk_size/2)) != 0 ) { printf("ERROR: LoadHashInputBinStr(): Hash in size %d must be evenly divisible by %d!\n", hash_in_len_bits, hash_in_chunk_size/2); fflush(stdout); exit(EXIT_FAILURE); } // Sanity check -- check for all '0's and '1's for ( i = 0; i < hash_in_len_bits; i++ ) if ( hash_in_str[i] != '0' && hash_in_str[i] != '1' ) { printf("ERROR: LoadHashInputBinStr(): Hash in values must be '0' or '1'!\n"); fflush(stdout); exit(EXIT_FAILURE); } // Reset the VHDL pointers to the hash in buffers. *CtrlRegA = ctrl_mask | (1 << OUT_CP_DTO_RESTART); *CtrlRegA = ctrl_mask; //printf("LoadHashInputBinStr(): Reset VHDL vector pointers!\n"); fflush(stdout); //printf("LoadHashInputBinStr(): Current control mask '%08X'\n", ctrl_mask); fflush(stdout); // Convert the 72-bit strings into binary values, 16-bits at a time, starting with the low order to high order bits. // Iterate over each of the 4 16-bit chunks + 1 8-bit chunk of string data. This division is 72/16 = 4.5 but truncated to an integer of 4 // We start with the 16 bit chunks first and then process the 8-bit chunk (high order, left most bits of string) in the last iteration for ( j = hash_in_len_bits/hash_in_chunk_size; j >= 0; j-- ) { // Go to 16-bit offset in hash_in string data. When j > 0, this is base + 4*16 = 72-8 = base + 64 (rightmost chunk of string data is low-order // component of hash). if ( j > 0 ) char_pos = hash_in_str + j*hash_in_chunk_size - 8; else char_pos = hash_in_str; //printf("LoadHashInputBinStr(): char_pos pointer '%s'!\n", char_pos); fflush(stdout); // Horner's method to compute binary value from '0' and '1's. WARNING: binary string from left to right is high-order // to low-order when interpreted as a binary value. HOWEVER, the storage of the binary string stores the high order bit // at the lowest address so we need to reverse the whole process. So to load the first low order 16-bit chunk (after the // VHDL pointers are initialized, we load the RndI[15:0]), we address the far right in the string with j. k then // addresses that portion of the string from high-order (lower address) to low-order (higher address). hash_in_val = 0; // High order chunk is only 8-bits if ( j == 0 ) i = hash_in_chunk_size/2; else i = hash_in_chunk_size; for ( k = 0; k < i; k++ ) { if ( char_pos[k] == '0' ) hash_in_val = hash_in_val*2; else hash_in_val = hash_in_val*2 + 1; //printf("LoadHashInputBinStr(): Processing %c\tCur 16-bit binary value in hex '%04X'\n", char_pos[k], hash_in_val); fflush(stdout); } printf("LoadHashInputBinStr(): 16-bit binary value in hex '%04X'\n", hash_in_val); fflush(stdout); // Four step protocol // 1) Assert 'data_ready' while putting the 16-bit binary value on the low order bits of CtrlReg //printf("LoadHashInputBinStr(): Writing 'data_ready' with 16-bit binary value in hex '%04X'\n", hash_in_val); fflush(stdout); *CtrlRegA = ctrl_mask | (1 << OUT_CP_DTO_DATA_READY) | hash_in_val; // 2) Wait for 'done_reading to go to 1 (it is low by default). State machine latches data in 2 clk cycles. // Maintain 1 on 'data_ready' and continue to hold 16-bit binary chunk. //printf("LoadHashInputBinStr(): Waiting state machine 'done_reading' to be set to '1'\n"); fflush(stdout); while ( (*DataRegA & (1 << IN_SM_DTO_DONE_READING)) == 0 ); // 3) Once 'done_reading' goes to 1, set 'data_ready' to 0 and remove chunk; //printf("LoadHashInputBinStr(): De-asserting 'data_ready'\n"); fflush(stdout); *CtrlRegA = ctrl_mask; // 4) Wait for 'done_reading to go to 0. //printf("LoadHashInputBinStr(): Waiting state machine 'done_reading' to be set to '0'\n"); fflush(stdout); while ( (*DataRegA & (1 << IN_SM_DTO_DONE_READING)) != 0 ); //printf("LoadHashInputBinStr(): Done handshake associated with vector chunk transfer\n"); fflush(stdout); } return; } /****************************************************************************/ /****************************** Word16ToBinary ******************************/ /****************************************************************************/ // WARNING: buffer is NOT NULL-terminated!!!!!!!!!!!!!1 void Word16ToBinary(int x, char *buffer) { int z, j; buffer[0] = '\0'; for ( z = 32768, j = 0; z > 0; z >>= 1, j++ ) if ((x & z) == z) buffer[j] = '1'; else buffer[j] = '0'; } /****************************************************************************/ /****************************** ComputeGetHash ******************************/ /****************************************************************************/ // Start the PUF engine with the compute the hash value. void ComputeGetHash(int str_length, volatile unsigned int *CtrlRegA, volatile unsigned int *DataRegA, int ctrl_mask, int hash_out_len_bits, int hash_out_chunk_size, char hash_out_str[hash_out_len_bits]) { int hash_out_val; int output_num; //printf("ComputeGetHash(): BEGIN\n"); fflush(stdout); // Sanity check. Hash engine MUST be 'ready' if ( (*DataRegA & (1 << IN_SM_READY)) == 0 ) { printf("ERROR: ComputeGetHash(): Hash Engine is NOT ready!\n"); fflush(stdout); exit(EXIT_FAILURE); } //printf("ComputeGetHash(): Hash engine is ready -- starting PUF engine!\n"); fflush(stdout); // Start the Hash engine. Hash input has already been loaded. *CtrlRegA = ctrl_mask | (1 << OUT_CP_START_HASH) | (1 << OUT_CP_FIRST_HASH); *CtrlRegA = ctrl_mask; //printf("ComputeGetHash(): Waiting PUF engine to finish!\n"); fflush(stdout); // Wait for the Hash engine to finish generating the timing data. while ( (*DataRegA & (1 << IN_SM_READY)) == 0 ); //printf("ComputeGetHash(): PUF engine DONE!\n"); fflush(stdout); // Reset the hash out pointers in the VHDL code. *CtrlRegA = ctrl_mask | (1 << OUT_CP_DTI_RESTART); *CtrlRegA = ctrl_mask; //printf("ComputeGetHash(): Checking 'data_ready' of DataTransferOut!\n"); fflush(stdout); // Wait for 'data_ready' to become 1 after the pointer reset (should already be 1). while ( (*DataRegA & (1 << IN_SM_DTI_DATA_READY)) == 0 ); //printf("ComputeGetHash(): 'data_ready' is set!\n"); fflush(stdout); // ================== // Get timing values for ( output_num = hash_out_len_bits/hash_out_chunk_size - 1; output_num >= 0; output_num-- ) { // shift can increase to 1120. NEED TO KEEP AN EYE ON THIS!!!!!!!!!!!!!!!!!!!!!!!! Save 400 FFs in the implementation // right now. Once we move to SRAM, increase this to 11 bits! hash_out_val = *DataRegA & 0xFFFF; Word16ToBinary(hash_out_val, &hash_out_str[output_num*hash_out_chunk_size]); // Four phases here. // 1) Got timing value above, indicate we are done reading. *CtrlRegA = ctrl_mask | (1 << OUT_CP_DTI_DONE_READING); // 2) Wait for 'data_ready' to become 0. while ( (*DataRegA & (1 << IN_SM_DTI_DATA_READY)) != 0 ); // 3) Reset done_reading to 0 *CtrlRegA = ctrl_mask; // 4) Wait for 'data_ready' to become 1. while ( (*DataRegA & (1 << IN_SM_DTI_DATA_READY)) == 0 ); } // NULL TERMINATE hash_out_str[hash_out_len_bits] = '\0'; return; } /****************************************************************************/ /**************************** SendTimings ***********************************/ /****************************************************************************/ // Send the data to the verifier through the socket void SendTimings(int str_length, int verifier_socket_desc, int vec_num, int num_non_zero_timing_vals, int *num_timing_vals_ptr, int max_outputs, int max_sams, short timing_val_arr[max_outputs][max_sams], short output_pos[max_outputs][max_sams], int num_sams, int change_rise_fall) { char timing_line[str_length]; char buffer[str_length]; int sam_num, nz_num; int num_lines; // Load up the timing samples to a string and send to the verifier. num_lines = 0; for ( nz_num = 0; nz_num < num_non_zero_timing_vals; nz_num++ ) { sprintf(timing_line, "V: %d\tO: %d\tC: %d\t", vec_num, output_pos[nz_num][0], *num_timing_vals_ptr); for ( sam_num = 0; sam_num < num_sams; sam_num++ ) { sprintf(buffer, " %d", timing_val_arr[nz_num][sam_num]); strcat(timing_line, buffer); // Sanity check: It must always be true that the sampled output remains the same across samples. if ( output_pos[nz_num][0] != output_pos[nz_num][sam_num] ) { printf("ERROR: SendTimings(): Output that produced timing value has CHANGED %d vs %d!\n", output_pos[nz_num][0], output_pos[nz_num][sam_num]); fflush(stdout); exit(EXIT_FAILURE); } } // Send timing value line with all samples included. Be sure to add '+1' to ensure NULL character is transferred to receiver. if ( SockSendB((unsigned char *)timing_line, strlen(timing_line) + 1, verifier_socket_desc) < 0 ) { printf("ERROR: SendTimings(): Send '%s' failed\n", timing_line); fflush(stdout); exit(EXIT_FAILURE); } // DEBUG //printf("SendTimings(): Current timing line[%d]:\n'%s'\n", *num_timing_vals_ptr, timing_line); fflush(stdout); num_lines++; (*num_timing_vals_ptr)++; } // Send the empty string which will cause a blank line to be inserted into the saved file on the receiver, as an indicator of when we change // from rising to falling (or vise versa). Be sure to add '+1' to ensure NULL character is transferred to receiver. if ( change_rise_fall == 1 ) if ( SockSendB((unsigned char *)"", strlen("") + 1, verifier_socket_desc) < 0 ) { printf("ERROR: SendTimings(): Send '' failed\n"); fflush(stdout); exit(EXIT_FAILURE); } // DEBUG printf("SendTimings(): Sent %d timing values for vector[%d]\n", nz_num, vec_num); fflush(stdout); } /**************************************************************************************/ /************************************** Main *****************************************/ /**************************************************************************************/ // For enrollment, the token is the 'server' while the verifier is the 'client' // MAX_OUTPUTS is the maximum number of simultaneously tested paths from the FU. #define MAX_OUTPUTS 400 #define MAX_STRING_LEN 2000 #define VEC_LEN_BITS 416 #define VEC_CHUNK_SIZE 16 #define HASH_IN_LEN_BITS 72 #define HASH_IN_CHUNK_SIZE 16 #define HASH_OUT_LEN_BITS 64 #define HASH_OUT_CHUNK_SIZE 16 #define MAX_VECS 15000 #define MAX_SAMS 128 short timing_val_arr[MAX_OUTPUTS][MAX_SAMS]; short output_pos[MAX_OUTPUTS][MAX_SAMS]; unsigned char *first_vecs_b[MAX_VECS]; unsigned char *second_vecs_b[MAX_VECS]; int main(int argc, char *argv[]) { volatile unsigned int *CtrlRegA; volatile unsigned int *DataRegA; // Prepare the sockaddr_in structure for the token; struct sockaddr_in verifier_addr; char token_IP[MAX_STRING_LEN]; int token_socket_desc = 0; int verifier_socket_desc = 0; int port_number; // Generic variables for ALL methods char chip_name[MAX_STRING_LEN]; char TV_corner_str[MAX_STRING_LEN]; char outfile_name[MAX_STRING_LEN]; int num_vecs, num_sams; int vec_num, sam_num; //Timing variables, Added by Wenjie struct timeval now1, now2; struct tm *subsec1, *subsec2; char timestr1[9], timestr2[9]; int rc1, rc2; char run_time_str[MAX_STRING_LEN]; int enable_labview; // These values represent the current source value to be applied to the MAXIM Vfb input that will result in // 0.95, 1.00, 1.05 (-5%, nominal, +5%). USED 10 corners IN 10/2x/2014 data collection, and in 4/5/2015. int max_TVs = 10; int temperatures[10] = {25, 25, 25, 25, 0, 0, 0, 85, 85, 85}; float voltages[10] = {1.00, 0.95, 1.00, 1.05, 0.95, 1.00, 1.05, 0.95, 1.00, 1.05}; float currents_uAs[10] = {-40e-6, -35e-6, -40e-6, -45e-6, -35e-6, -40e-6, -45e-6, -35e-6, -40e-6, -45e-6}; int TV_num, num_TVs, start_TV, end_TV; char TV_info_str[MAX_STRING_LEN]; int num_timing_vals, num_rise_vecs; int first_num_non_zero_timing_vals, num_non_zero_timing_vals; char hash_in_str[HASH_IN_LEN_BITS+1]; char hash_out_str[HASH_OUT_LEN_BITS+1]; int ctrl_mask; // ====================================================================================================================== // COMMAND LINE if ( argc != 6 ) { printf("ERROR: token_enrollment.elf(): Chip name (C1) -- start TV (0) -- end TV (9) -- labview present (0/1) -- token_IP\n"); return(1); } sscanf(argv[1], "%s", chip_name); sscanf(argv[2], "%d", &start_TV); sscanf(argv[3], "%d", &end_TV); sscanf(argv[4], "%d", &enable_labview); strcpy(token_IP, argv[5]); // ====================================================== PARAMETERS ==================================================== num_sams = 16; port_number = 8888; // ====================================================================================================================== // ====================================================================================================================== if ( start_TV < 0 || end_TV >= max_TVs ) { printf("ERROR: start and end TV MUST be bound between 0 and MAX-1 -> %d\n", max_TVs - 1); exit(EXIT_FAILURE); } num_TVs = end_TV - start_TV + 1; // ========================================= // Redirect standard input and output (and error?) to the serial port if enable_labview is 1. NOTE: After booting linux up, you // MUST also kill the shell that runs on /dev/ttyPS0 port by commenting out the line in the /etc/inittab file, issueing 'kill -1 1' // and then killing the /bin/ash shell whose PID is given by 'ps aux'. if ( enable_labview == 1 ) { freopen("/dev/ttyPS0", "w", stdout); freopen("/dev/ttyPS0", "r", stdin); } freopen("output.log", "w", stderr); // 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 offset of 8 bytes for 32-bit integer variables) DataRegA = mmap(0, getpagesize(), PROT_READ|PROT_WRITE, MAP_SHARED, fd, GPIO_0_BASE_ADDR); CtrlRegA = DataRegA + 2; // Serve-up a socket connection to the verifier. OpenSocketServer(MAX_STRING_LEN, &token_socket_desc, token_IP, port_number, &verifier_socket_desc, &verifier_addr, 0); // Read all the vectors from the verifier into a set of string arrays. num_vecs = ReceiveEnrollVectors(MAX_STRING_LEN, verifier_socket_desc, MAX_VECS, first_vecs_b, second_vecs_b, VEC_LEN_BITS, &num_rise_vecs); printf("Number of vectors read %d\n", num_vecs); fflush(stdout); // ============================================================================================================ // TV LOOP // ============================================================================================================ // Repeat the analysis across n TV corners. for ( TV_num = start_TV; TV_num <= end_TV; TV_num++ ) { sprintf(TV_corner_str, "%dC_%.2fV", temperatures[TV_num], voltages[TV_num]); // Construct the output filenames. (BUG, 3/24/2016 -- class code has this bug -- used to be strcat) strcpy(outfile_name, chip_name); strcat(outfile_name, "_"); strcat(outfile_name, TV_corner_str); // Distinguish between 2 25C corners -- enroll and regeneration if ( TV_num == 0 ) strcat(outfile_name, "_E"); else strcat(outfile_name, "_R"); strcat(outfile_name, "_PUFNums.txt"); // Be sure to add '+1' to ensure NULL character is transferred to receiver. if ( SockSendB((unsigned char *)outfile_name, strlen(outfile_name) + 1, verifier_socket_desc) < 0 ) { printf("ERROR: Send '%s' failed\n", outfile_name); fflush(stdout); exit(EXIT_FAILURE); } // ------------------------------------------------------------------------------------- // Send TV corner info. Don't forget the '\n'! sprintf(TV_info_str, "TV %dC %.2fV (%d) of total TVs %d\n", temperatures[TV_num], voltages[TV_num], TV_num, num_TVs); printf("\n%s\n", TV_info_str); fflush(stdout); // ------------------------------------------------------------------------------------- // Set the TV condition. if ( enable_labview == 1 ) SetTVCorner(MAX_STRING_LEN, TV_num, max_TVs, temperatures, currents_uAs, verifier_socket_desc); // ------------------------------------------------------------------------------------- // Allow time for voltage to settle (1/2 second) usleep(500000); // Run time information rc1 = gettimeofday(&now1, 0); if (rc1 != 0) { perror("gettimeofday"); return 1; } subsec1 = localtime(&now1.tv_sec); if (subsec1 == 0) { perror("localtime"); return 1; } rc1 = strftime(timestr1, sizeof(timestr1), "%H:%M:%S", subsec1); if (rc1 == 0) { printf("strftime call failed.\n"); return 1; } // Do a soft RESET *CtrlRegA = (1 << OUT_CP_RESET); *CtrlRegA = 0; usleep (10000); // Set the control mask to indicate PUF mode ctrl_mask = (1 << OUT_CP_MODE_FUNC_PUF); // ================== // ================== // Repeat for n vectors. num_timing_vals = 0; for ( vec_num = 0; vec_num < num_vecs; vec_num++ ) { printf("Processing vector number %d\n", vec_num); fflush(stdout); // Load up the 2 vectors into the VHDL buffers for LCI testing. LoadVecPair(MAX_STRING_LEN, CtrlRegA, DataRegA, first_vecs_b[vec_num], second_vecs_b[vec_num], ctrl_mask, VEC_LEN_BITS, VEC_CHUNK_SIZE); // Repeat for n samples. for ( sam_num = 0; sam_num < num_sams; sam_num++ ) { num_non_zero_timing_vals = GenGetTimingVals(CtrlRegA, DataRegA, MAX_OUTPUTS, MAX_SAMS, timing_val_arr, output_pos, sam_num, ctrl_mask); // Sanity check. Same paths must be timed across ALL 16 samples. if ( sam_num == 0 ) first_num_non_zero_timing_vals = num_non_zero_timing_vals; else if ( first_num_non_zero_timing_vals != num_non_zero_timing_vals ) { printf("ERROR: Number of paths timed for first sample is NOT equal for other samples!\n"); fflush(stdout); exit(EXIT_FAILURE); } } // Dump out the data to the verifier through the accepted socket. Last parameter adds a (blank line) to the // timing values sent back to the verifier after this last rising edge vector is applied. SendTimings(MAX_STRING_LEN, verifier_socket_desc, vec_num, num_non_zero_timing_vals, &num_timing_vals, MAX_OUTPUTS, MAX_SAMS, timing_val_arr, output_pos, num_sams, (vec_num == num_rise_vecs - 1)); } // Run time information rc2 = gettimeofday(&now2, 0); if (rc2 != 0) { perror("gettimeofday"); return 1; } subsec2 = localtime(&now2.tv_sec); if (subsec2 == 0) { perror("localtime"); return 1; } rc2 = strftime(timestr2, sizeof(timestr2), "%H:%M:%S", subsec2); if (rc2 == 0) { printf("strftime call failed.\n"); return 1; } int t2, t1; t2 = atoi (&(timestr2[6])); t1 = atoi (&(timestr1[6])); // Send run time information ending with a "DONE" string. if (now2.tv_usec - now1.tv_usec <= 0) sprintf(run_time_str, "TV corner %d\tTOTAL_RUNNING_TIME %d.%06ld\nDONE\n", TV_num, t2 - t1 -1, now2.tv_usec - now1.tv_usec + 1000000 ); else sprintf(run_time_str, "TV corner %d\tTOTAL_RUNNING_TIME %d.%06ld\nDONE\n", TV_num, t2 - t1, now2.tv_usec - now1.tv_usec); printf("\n%s\n", run_time_str); fflush(stdout); // Be sure to add '+1' to ensure NULL character is transferred to receiver. if ( SockSendB((unsigned char *)"DONE", strlen("DONE") + 1, verifier_socket_desc) < 0 ) { printf("ERROR: Send 'DONE' failed\n"); fflush(stdout); exit(EXIT_FAILURE); } // =========================================================================================================================== // Test hash function // Load up the a hash 72-bit input value // Set the control mask to indicate HASH mode ctrl_mask = 0; // Answer: 98 c0 4c 8b 29 f4 62 06 strcpy(hash_in_str, "110000000000000000000000000000000000000000000000000000000000000000000000"); LoadHashInputBinStr(MAX_STRING_LEN, CtrlRegA, DataRegA, hash_in_str, ctrl_mask, HASH_IN_LEN_BITS, HASH_IN_CHUNK_SIZE); // Compute the hash ComputeGetHash(MAX_STRING_LEN, CtrlRegA, DataRegA, ctrl_mask, HASH_OUT_LEN_BITS, HASH_OUT_CHUNK_SIZE, hash_out_str); printf("Hash Input '%s' => Hash Output '%s'\n", hash_in_str, hash_out_str); fflush(stdout); // On all but last iteration, close socket // if ( TV_num < end_TV ) // close(verifier_socket_desc); } // Took this out because starting the cool-down after the last data set is collected resets the FPGA // You MUST MANUALLY set the temperature back to 25C AFTER you transfer the data. // SetTVCorner(MAX_STRING_LEN, 0, max_TVs, temperatures, currents_uAs, verifier_socket_desc); close(verifier_socket_desc); close(token_socket_desc); fclose(stderr); return 0; }