bool set_otp_zone (int fd, struct octet_buffer *otp_zone) { assert (NULL != otp_zone); const unsigned int SIZE_OF_WRITE = 32; /* The device must be using an OTP read only mode */ if (!is_otp_read_only_mode (fd)) assert (false); /* The writes must be done in 32 bytes blocks */ uint8_t nulls[SIZE_OF_WRITE]; uint8_t part1[SIZE_OF_WRITE]; uint8_t part2[SIZE_OF_WRITE]; struct octet_buffer buf ={}; wipe (nulls, SIZE_OF_WRITE); wipe (part1, SIZE_OF_WRITE); wipe (part2, SIZE_OF_WRITE); /* Simple check to make sure PACKAGE_VERSION isn't too long */ assert (strlen (PACKAGE_VERSION) < 10); /* Setup the fixed OTP data zone */ sprintf ((char *)part1, "CRYPTOTRONIX HASHLET REV: A"); sprintf ((char *)part2, "SOFTWARE VERSION: %s", PACKAGE_VERSION); bool success = true; buf.ptr = nulls; buf.len = sizeof (nulls); /* Fill the OTP zone with blanks from their default FFFF */ success = write32 (fd, OTP_ZONE, 0, buf); if (success) success = write32 (fd, OTP_ZONE, SIZE_OF_WRITE / sizeof (uint32_t), buf); /* Fill in the data */ buf.ptr = part1; CTX_LOG (DEBUG, "Writing: %s", buf.ptr); if (success) success = write32 (fd, OTP_ZONE, 0, buf); buf.ptr = part2; CTX_LOG (DEBUG, "Writing: %s", buf.ptr); if (success) success = write32 (fd, OTP_ZONE, SIZE_OF_WRITE / sizeof (uint32_t), buf); /* Lastly, copy the OTP zone into one contiguous buffer. Ironically, the OTP can't be read while unlocked. */ if (success) { otp_zone->len = SIZE_OF_WRITE * 2; otp_zone->ptr = malloc_wipe (otp_zone->len); memcpy (otp_zone->ptr, part1, SIZE_OF_WRITE); memcpy (otp_zone->ptr + SIZE_OF_WRITE, part2, SIZE_OF_WRITE); } return success; }
bool lock (int fd, enum DATA_ZONE zone, uint16_t crc) { uint8_t param1 = 0; uint8_t param2[2]; uint8_t response; bool result = false; if (is_locked (fd, zone)) return true; memcpy (param2, &crc, sizeof (param2)); const uint8_t CONFIG_MASK = 0; const uint8_t DATA_MASK = 1; switch (zone) { case CONFIG_ZONE: param1 |= CONFIG_MASK; break; case DATA_ZONE: case OTP_ZONE: param1 |= DATA_MASK; break; default: assert (false); } struct Command_ATSHA204 c = make_command (); set_opcode (&c, COMMAND_LOCK); set_param1 (&c, param1); set_param2 (&c, param2); set_data (&c, NULL, 0); set_execution_time (&c, 0, LOCK_AVG_EXEC); if (RSP_SUCCESS == process_command (fd, &c, &response, sizeof (response))) { if (0 == response) { result = true; CTX_LOG (DEBUG, "Lock Successful"); } else { CTX_LOG (DEBUG, "Lock Failed"); } } return result; }
struct octet_buffer get_random (int fd, bool update_seed) { uint8_t *random = NULL; uint8_t param2[2] = {0}; uint8_t param1 = update_seed ? 0 : 1; struct octet_buffer buf = {}; random = malloc_wipe (RANDOM_RSP_LENGTH); struct Command_ATSHA204 c = make_command (); set_opcode (&c, COMMAND_RANDOM); set_param1 (&c, param1); set_param2 (&c, param2); set_data (&c, NULL, 0); set_execution_time (&c, 0, RANDOM_AVG_EXEC); if (RSP_SUCCESS == process_command (fd, &c, random, RANDOM_RSP_LENGTH)) { buf.ptr = random; buf.len = RANDOM_RSP_LENGTH; } else CTX_LOG (DEBUG, "Random command failed"); return buf; }
void Context::handleSignal(Signal::Type stype) { CTX_LOG(info, "Requesting shutdown in response to " << Signal::typeAsString(stype)); // Try to keep this minimal. Post the shutdown process rather than // actually running it here. This makes the extent of the signal // handling known completely in this method, whereas calling // shutdown can cause a cascade of cleanup. ioService->post( std::tr1::bind(&Context::shutdown, this), "Context::shutdown" ); }
void Context::shutdown() { CTX_LOG(info, "Handling shutdown request"); Signal::unregisterHandler(mSignalHandler); this->stop(); for(std::vector<Service*>::iterator it = mServices.begin(); it != mServices.end(); it++) (*it)->stop(); // If the original thread wasn't running this context as well, then it won't // be able to wait for the worker threads it created. if (mExecutionThreadsType != IncludeOriginal) cleanupWorkerThreads(); }
bool record_keys (struct key_container *keys) { assert (NULL != keys); bool result = false; FILE *f = NULL; struct passwd *pw = getpwuid (getuid ()); assert (NULL != pw); const char *home = pw->pw_dir; unsigned int filename_len = strlen (home) + strlen (KEY_STORE) + 1; char *filename = (char *)malloc_wipe (filename_len); strcpy (filename, home); strcat (filename, KEY_STORE); if ((f = fopen (filename, "w")) != NULL) { fprintf (f, "# Hashlet key store written from version: %s\n", PACKAGE_VERSION); unsigned int x = 0; for (x=0; x< get_max_keys (); x++) { fprintf (f, "key_slot_%02u ",x); unsigned int y = 0; for (y=0; y < keys->keys[x].len; y++) { fprintf (f, "%02X", keys->keys[x].ptr[y]); } fprintf (f, "%s", "\n"); } fclose (f); result = true; } else { CTX_LOG (INFO, "Failed to open %s", filename); perror ("Error"); } free_wipe ((uint8_t *)filename, filename_len); return result; }
unsigned int serialize_command (struct Command_ATSHA204 *c, uint8_t **serialized) { unsigned int total_len = 0; unsigned int crc_len = 0; unsigned int crc_offset = 0; uint8_t *data; uint16_t *crc; assert (NULL != c); assert (NULL != serialized); total_len = sizeof (c->command) + sizeof (c->count) +sizeof (c->opcode) + sizeof (c->param1) + sizeof (c->param2) + c->data_len + sizeof (c->checksum); crc_len = total_len - sizeof (c->command) - sizeof (c->checksum); crc_offset = total_len - sizeof (c->checksum); c->count = total_len - sizeof (c->command); data = (uint8_t*)malloc (total_len); assert (NULL != data); print_command (c); CTX_LOG (DEBUG, "Total len: %d, count: %d, CRC_LEN: %d, CRC_OFFSET: %d\n", total_len, c->count, crc_len, crc_offset); /* copy over the command */ data[0] = c->command; data[1] = c->count; data[2] = c->opcode; data[3] = c->param1; data[4] = c->param2[0]; data[5] = c->param2[1]; if (c->data_len > 0) memcpy (&data[6], c->data, c->data_len); crc = (uint16_t *)&data[crc_offset]; *crc = calculate_crc16 (&data[1], crc_len); *serialized = data; return total_len; }
enum STATUS_RESPONSE get_status_response(const uint8_t *rsp) { const unsigned int OFFSET_TO_CRC = 2; const unsigned int OFFSET_TO_RSP = 1; const unsigned int STATUS_LENGTH = 4; if (!is_crc_16_valid (rsp, STATUS_LENGTH - CRC_16_LEN, rsp + OFFSET_TO_CRC)) { CTX_LOG (DEBUG, "CRC Fail in status response"); return RSP_COMM_ERROR; } return *(rsp + OFFSET_TO_RSP); }
enum STATUS_RESPONSE send_and_receive (int fd, uint8_t *send_buf, unsigned int send_buf_len, uint8_t *recv_buf, unsigned int recv_buf_len, struct timespec *wait_time) { struct timespec tim_rem; enum STATUS_RESPONSE rsp = RSP_AWAKE; const unsigned int NUM_RETRIES = 10; unsigned int x = 0; ssize_t result = 0; assert (NULL != send_buf); assert (NULL != recv_buf); assert (NULL != wait_time); /* Send the data at first. During a read, if the device responds with an "I'm Awake" flag, we've lost synchronization, so send the data again in that case only. Arbitrarily retry this procedure NUM_RETRIES times */ for (x=0; x < NUM_RETRIES && rsp == RSP_AWAKE; x++) { print_hex_string ("Sending", send_buf, send_buf_len); result = i2c_write (fd,send_buf,send_buf_len); if (result > 1) { do { nanosleep (wait_time , &tim_rem); } while ((rsp = read_and_validate (fd, recv_buf, recv_buf_len)) == RSP_NAK); CTX_LOG (DEBUG, "Command Response: %s", status_to_string (rsp)); } else { perror ("Send failed\n"); exit (1); } } return rsp; }
bool wakeup(int fd) { uint32_t wakeup = 0; unsigned char buf[4] = {0}; bool awake = false; /* The assumption here that the fd is the i2c fd. Of course, it may * not be, so this may loop for a while (read forever). This should * probably try for only so often before quitting. */ /* Perform a basic check to see if this fd is open. This does not guarantee it is the correct fd */ if(fcntl(fd, F_GETFD) < 0) perror("Invalid FD.\n"); while (!awake) { if (write(fd,&wakeup,sizeof(wakeup)) > 1) { CTX_LOG(DEBUG, "%s", "Device is awake."); // Using I2C Read if (read(fd,buf,sizeof(buf)) <= 0) { /* ERROR HANDLING: i2c transaction failed */ perror("Failed to read from the i2c bus.\n"); } else { assert(is_crc_16_valid(buf, 2, buf+2)); awake = true; } } } return awake; }
Context::Context(const String& name_, Network::IOService* ios, Network::IOStrand* strand, Trace::Trace* _trace, const Time& epoch, const Duration& simlen) : name(name_), ioService(ios), mainStrand(strand), profiler(NULL), timeSeries(NULL), mFinishedTimer( Network::IOTimer::create(ios) ), mTrace(_trace), mCommander(NULL), mEpoch(epoch), mLastSimTime(Time::null()), mSimDuration(simlen), mKillThread(), mKillService(NULL), mKillTimer(), mStopRequested(false) { CTX_LOG(info, "Creating context"); Breakpad::init(); profiler = new TimeProfiler(this, name); }
void Context::run(uint32 nthreads, ExecutionThreads exthreads) { CTX_LOG(info, "Starting context execution with " << nthreads << " threads"); mExecutionThreadsType = exthreads; uint32 nworkers = (exthreads == IncludeOriginal ? nthreads-1 : nthreads); // Start workers for(uint32 i = 0; i < nworkers; i++) { mWorkerThreads.push_back( new Thread( name + " Worker " + boost::lexical_cast<String>(i), std::tr1::bind(&Context::workerThread, this) ) ); } // Run if (exthreads == IncludeOriginal) { ioService->run(); cleanupWorkerThreads(); // If we exited gracefully, call shutdown automatically to clean everything // up, make sure stop() methods get called, etc. if (!mStopRequested.read()) this->shutdown(); } }
Context::~Context() { CTX_LOG(info, "Destroying context"); delete profiler; }
enum STATUS_RESPONSE read_and_validate (int fd, uint8_t *buf, unsigned int len) { uint8_t* tmp = NULL; const int PAYLOAD_LEN_SIZE = 1; const int CRC_SIZE = 2; enum STATUS_RESPONSE status = RSP_COMM_ERROR; unsigned int recv_buf_len = 0; bool crc_valid; unsigned int crc_offset; int read_bytes; const unsigned int STATUS_RSP = 4; assert (NULL != buf); recv_buf_len = len + PAYLOAD_LEN_SIZE + CRC_SIZE; crc_offset = recv_buf_len - 2; /* The buffer that comes back has a length byte at the front and a * two byte crc at the end. */ tmp = malloc_wipe (recv_buf_len); read_bytes = i2c_read (fd, tmp, recv_buf_len); /* First Case: We've read the buffer and it's a status packet */ if (read_bytes == recv_buf_len && tmp[0] == STATUS_RSP) { print_hex_string ("Status RSP", tmp, tmp[0]); status = get_status_response (tmp); CTX_LOG (DEBUG, status_to_string (status)); } /* Second case: We received the expected message length */ else if (read_bytes == recv_buf_len && tmp[0] == recv_buf_len) { print_hex_string ("Received RSP", tmp, recv_buf_len); crc_valid = is_crc_16_valid (tmp, tmp[0] - CRC_16_LEN, tmp + crc_offset); if (true == crc_valid) { wipe (buf, len); memcpy (buf, &tmp[1], len); status = RSP_SUCCESS; } else { perror ("CRC FAIL!\n"); } } else { CTX_LOG (DEBUG,"Read failed, retrying"); status = RSP_NAK; } free_wipe (tmp, recv_buf_len); return status; }
void print_command (struct Command_ATSHA204 *c) { assert (NULL != c); const char* opcode = NULL; CTX_LOG (DEBUG, "*** Printing Command ***"); CTX_LOG (DEBUG, "Command: 0x%02X", c->command); CTX_LOG (DEBUG, "Count: 0x%02X", c->count); CTX_LOG (DEBUG, "OpCode: 0x%02X", c->opcode); switch (c->opcode) { case COMMAND_DERIVE_KEY: opcode = "Command Derive Key"; break; case COMMAND_DEV_REV: opcode = "Command Dev Rev"; break; case COMMAND_GEN_DIG: opcode = "Command Generate Digest"; break; case COMMAND_HMAC: opcode = "Command HMAC"; break; case COMMAND_CHECK_MAC: opcode = "Command Check MAC"; break; case COMMAND_LOCK: opcode = "Command Lock"; break; case COMMAND_MAC: opcode = "Command MAC"; break; case COMMAND_NONCE: opcode = "Command NONCE"; break; case COMMAND_PAUSE: opcode = "Command Pause"; break; case COMMAND_RANDOM: opcode = "Command Random"; break; case COMMAND_READ: opcode = "Command Read"; break; case COMMAND_UPDATE_EXTRA: opcode = "Command Update Extra"; break; case COMMAND_WRITE: opcode = "Command Write"; break; default: assert (false); } CTX_LOG (DEBUG,"%s", opcode); CTX_LOG (DEBUG,"param1: 0x%02X", c->param1); CTX_LOG (DEBUG,"param2: 0x%02X 0x%02X", c->param2[0], c->param2[1]); if (c->data_len > 0) print_hex_string ("Data", c->data, c->data_len); CTX_LOG (DEBUG,"CRC: 0x%02X 0x%02X", c->checksum[0], c->checksum[1]); CTX_LOG (DEBUG,"Wait time: %ld seconds %lu nanoseconds", c->exec_time.tv_sec, c->exec_time.tv_nsec); }
/* Parse a single option. */ static error_t parse_opt (int key, char *arg, struct argp_state *state) { /* Get the input argument from argp_parse, which we know is a pointer to our arguments structure. */ struct arguments *arguments = state->input; int slot; long int address_arg; switch (key) { case 'a': /* TODO: Not working as expected */ address_arg = strtol (arg,NULL,16); if (0 != address_arg) { arguments->address = address_arg; CTX_LOG (DEBUG, "Using address %u", address_arg); } else CTX_LOG (INFO, "Address not recognized, using default"); break; case 'b': arguments->bus = arg; break; case 'B': arguments->bytes = atoi(arg); break; case 'q': case 's': arguments->silent = 1; break; case 'v': arguments->verbose = 1; set_log_level (DEBUG); break; case 'f': arguments->input_file = arg; break; case OPT_UPDATE_SEED: arguments->update_seed = true; break; case 'k': slot = atoi (arg); if (slot < 0 || slot > 15) argp_usage (state); arguments->key_slot = slot; break; case 'c': if (!is_hex_arg (arg, 64)) { fprintf (stderr, "%s\n", "Invalid Challenge."); argp_usage (state); } else arguments->challenge = arg; break; case 'w': if (!is_hex_arg (arg, 64)) { fprintf (stderr, "%s\n", "Invalid Data."); argp_usage (state); } else arguments->write_data = arg; break; case 'r': if (!is_hex_arg (arg, 64)) { fprintf (stderr, "%s\n", "Invalid Challenge Response."); argp_usage (state); } else arguments->challenge_rsp = arg; break; case 'm': if (!is_hex_arg (arg, 26)) { fprintf (stderr, "%s\n", "Invalid Meta Data."); argp_usage (state); } else arguments->meta = arg; break; case ARGP_KEY_ARG: if (state->arg_num > NUM_ARGS) { /* Too many arguments. */ argp_usage (state); } else { arguments->args[state->arg_num] = arg; } break; case ARGP_KEY_END: if (state->arg_num < NUM_ARGS) /* Not enough arguments. */ argp_usage (state); break; default: return ARGP_ERR_UNKNOWN; } return 0; }
bool write_keys (int fd, struct key_container *keys, struct octet_buffer *data_zone) { assert (NULL != data_zone); assert (STATE_INITIALIZED == get_device_state (fd)); bool free_keys = false; #define KEY_LEN 32 const unsigned int TEST_KEY_1 = 14; const unsigned int TEST_KEY_2 = 15; struct octet_buffer test_key_14 = make_buffer (KEY_LEN); memset(test_key_14.ptr, 0xAA, KEY_LEN); struct octet_buffer test_key_15 = make_buffer (KEY_LEN); memset(test_key_15.ptr, 0xBB, KEY_LEN); int x = 0; /* If there are no keys, which is the case when we are personalizing a device from scratch, create some new keys */ if (NULL == keys) { keys = make_key_container (); for (x=0; x < get_max_keys (); x++) { if (TEST_KEY_1 == x) { keys->keys[x] = test_key_14; } else if (TEST_KEY_2 == x) { keys->keys[x] = test_key_15; } else { keys->keys[x] = get_random (fd, false); } } free_keys = true; } bool status = true; for (x=0; x < get_max_keys () && status; x++) { const unsigned int WORD_OFFSET = 8; unsigned int addr = WORD_OFFSET * x; status = write32 (fd, DATA_ZONE, addr, keys->keys[x], NULL); } if (status) { data_zone->len = get_max_keys () * keys->keys[0].len; data_zone->ptr = malloc_wipe (data_zone->len); for (x=0; x < get_max_keys (); x++) { CTX_LOG(DEBUG, "Writing key %u", x); unsigned int offset = x * keys->keys[x].len; memcpy(data_zone->ptr + offset, keys->keys[x].ptr, keys->keys[x].len); } status = record_keys (keys); } if (free_keys) free_key_container (keys); return status; }