def train(challenges, responses, t_pairs): try: with open('weights.txt', 'rb') as f: weights = np.load(f) except: print("[*] ENOWEIGHTS") # create instance frome same weights for accuracy calculation instance = LTFArray( weight_array=weights, transform=LTFArray.transform_atf, combiner=LTFArray.combiner_xor, ) # train model from obtained CRPs training_set = tools.ChallengeResponseSet(challenges, responses) lr_learner = LogisticRegression( t_set=training_set, n=48, k=4, transformation=LTFArray.transform_atf, combiner=LTFArray.combiner_xor, ) # learn and test the model model = lr_learner.learn() accuracy = 1 - tools.approx_dist(instance, model, 10000) print('Learned a 48-bit 4-xor XOR Arbiter PUF from {} CRPs with accuracy {}'.format(t_pairs, accuracy)) return model
def pipeline(N): instance = LTFArray( weight_array=LTFArray.normal_weights(n=64, k=2), transform=LTFArray.transform_atf, combiner=LTFArray.combiner_xor ) train_set = tools.TrainingSet(instance=instance, N=N) train_size = int(len(train_set.challenges) * 0.95) val_set = train_set.subset(slice(train_size, None)) train_set = train_set.subset(slice(None, train_size)) lr_learner = LogisticRegression( t_set=train_set, n=64, k=2, transformation=LTFArray.transform_atf, combiner=LTFArray.combiner_xor, ) model = lr_learner.learn() val_set_predicted_responses = model.eval(val_set.challenges) accuracy = accuracy_score(val_set_predicted_responses, val_set.responses) return accuracy
def test_learn_ip_mod2(self): """" Stupid test which gains code coverage """ instance_prng = RandomState(seed=TestLogisticRegression.seed_instance) model_prng = RandomState(seed=TestLogisticRegression.seed_model) instance = LTFArray( weight_array=LTFArray.normal_weights( TestLogisticRegression.n, TestLogisticRegression.k, random_instance=instance_prng), transform=LTFArray.transform_id, combiner=LTFArray.combiner_ip_mod2, ) lr_learner = LogisticRegression( TrainingSet(instance=instance, N=TestLogisticRegression.N), TestLogisticRegression.n, TestLogisticRegression.k, transformation=LTFArray.transform_id, combiner=LTFArray.combiner_xor, weights_prng=model_prng, ) lr_learner.learn()
def main(): """ Run an example how to use pypuf. Developers Notice: Changes here need to be mirrored to README! """ # create a simulation with random (Gaussian) weights # for 64-bit 4-XOR instance = LTFArray( weight_array=LTFArray.normal_weights(n=64, k=2), transform=LTFArray.transform_atf, combiner=LTFArray.combiner_xor, ) # create the learner lr_learner = LogisticRegression( t_set=tools.TrainingSet(instance=instance, N=12000), n=64, k=2, transformation=LTFArray.transform_atf, combiner=LTFArray.combiner_xor, ) # learn and test the model model = lr_learner.learn() accuracy = 1 - tools.approx_dist(instance, model, 10000) # output the result print('Learned a 64bit 2-xor XOR Arbiter PUF from 12000 CRPs with accuracy %f' % accuracy)
def prepare(self): """ Initializes the instance, the training set and the learner to then run the logistic regression with the given parameters. """ self.instance = LTFArray( weight_array=LTFArray.normal_weights( self.parameters.n, self.parameters.k, random_instance=RandomState( seed=self.parameters.seed_instance)), transform=self.parameters.transformation, combiner=self.parameters.combiner, ) self.learner = LogisticRegression( tools.TrainingSet(instance=self.instance, N=self.parameters.N, random_instance=RandomState( self.parameters.seed_challenge)), self.parameters.n, self.parameters.k, transformation=self.instance.transform, combiner=self.instance.combiner, weights_prng=RandomState(seed=self.parameters.seed_model), logger=self.progress_logger, minibatch_size=self.parameters.mini_batch_size, convergance_decimals=self.parameters.convergence_decimals or 2, shuffle=self.parameters.shuffle, )
def prepare(self): super().prepare() self.learner = LogisticRegression( t_set=TrainingSet(instance=self.ltf_array, N=self.parameters.N), n=self.parameters.n, k=self.parameters.k, transformation=self.ltf_array.transform, combiner=self.ltf_array.combiner, convergence_decimals=100, # never converge iteration_limit=20, )
def main(): """ Learns and evaluates a PUF. """ parser = argparse.ArgumentParser() parser.add_argument('n', type=uint, help='challenge bits') parser.add_argument('k', type=uint, help='number of arbiter chains') parser.add_argument('num_tr', type=uint, help='number of CRPs to use for training') parser.add_argument('num_te', type=uint, help='number of CRPs to use for testing') parser.add_argument('file', type=str, help='file to read CRPs from') parser.add_argument('-1', '--11-notation', dest='in_11_notation', action='store_true', default=False, help='file is in -1,1 notation (default is 0,1)') args = parser.parse_args() # read pairs from file training_set = tools.parse_file(args.file, args.n, 1, args.num_tr, args.in_11_notation) testing_set = tools.parse_file(args.file, args.n, args.num_tr + 1, args.num_te, args.in_11_notation) # create the learner lr_learner = LogisticRegression( t_set=training_set, n=args.n, k=args.k, transformation=LTFArray.transform_atf, combiner=LTFArray.combiner_xor, ) # learn and test the model model = lr_learner.learn() accuracy = 1 - tools.approx_dist_nonrandom(model, testing_set) # output the result print( 'Learned a {}-bit {}-xor XOR Arbiter PUF from {} CRPs with accuracy {}' .format(args.n, args.k, args.num_tr, accuracy))
def run(self): """ Initializes the instance, the training set and the learner to then run the logistic regression with the given parameters. """ # TODO input transformation is computed twice. Add a shortcut to recycle results from the first computation self.instance = LTFArray( weight_array=LTFArray.normal_weights(self.n, self.k, random_instance=self.instance_prng), transform=self.transformation, combiner=self.combiner, ) self.learner = LogisticRegression( tools.TrainingSet(instance=self.instance, N=self.N, random_instance=self.challenge_prng), self.n, self.k, transformation=self.transformation, combiner=self.combiner, weights_prng=self.model_prng, logger=self.progress_logger, ) self.model = self.learner.learn()
class LRBenchmarkExperiment(LTFBenchmarkExperiment): """ Measures the time the logistic regression learner takes to learn a target. """ def __init__(self, progress_log_prefix, parameters): super().__init__(progress_log_prefix, parameters) self.learner = None def prepare(self): super().prepare() self.learner = LogisticRegression( t_set=TrainingSet(instance=self.ltf_array, N=self.parameters.N), n=self.parameters.n, k=self.parameters.k, transformation=self.ltf_array.transform, combiner=self.ltf_array.combiner, convergence_decimals=100, # never converge iteration_limit=20, ) def run(self): self.learner.learn()
def train(): instance = LTFArray( weight_array=LTFArray.normal_weights(n=48, k=4), transform=LTFArray.transform_atf, combiner=LTFArray.combiner_xor, ) N = 18000 # learn and test the model lr_learner = LogisticRegression( t_set=tools.TrainingSet(instance=instance, N=N), n=48, k=4, transformation=LTFArray.transform_atf, combiner=LTFArray.combiner_xor, ) model = lr_learner.learn() accuracy = 1 - tools.approx_dist(instance, model, 10000) print( 'Learned a 48bit 4-xor XOR Arbiter PUF from %d CRPs with accuracy %f' % (N, accuracy))
class CorrelationAttack(Learner): """ Learn an LTF-Array that uses the transform_lightweight_secure. This Attack uses Logistic Regression (LR) to learn an initial model and subsequently exploits the correlation between the weight arrays to restart the LR learner on a permuted model. This leads to a faster retrieval of high-accuracy models than pure LR. """ OPTIMIZATION_ACCURACY_LOWER_BOUND = .65 OPTIMIZATION_ACCURACY_UPPER_BOUND = .95 OPTIMIZATION_ACCURACY_GOAL = .98 def __init__(self, n, k, training_set, validation_set, weights_mu=0, weights_sigma=1, weights_prng=RandomState(), lr_iteration_limit=1000, mini_batch_size=0, convergence_decimals=2, shuffle=False, logger=None): """ Initialize a Correlation Attack Learner for the specified LTF Array which uses transform_lightweight_secure. :param n: Input length :param k: Number of parallel LTFs in the LTF Array :param training_set: The training set, i.e. a data structure containing challenge response pairs :param validation_set: The validation set, i.e. a data structure containing challenge response pairs. Used for approximating accuracies of permuted models (can be smaller e.g. 0.1*training_set_size) :param weights_mu: mean of the Gaussian that is used to choose the initial model :param weights_sigma: standard deviation of the Gaussian that is used to choose the initial model :param weights_prng: PRNG to draw the initial model from. Defaults to fresh `numpy.random.RandomState` instance. :param lr_iteration_limit: Iteration limit for a single LR learner run :param logger: logging.Logger Logger which is used to log detailed information of learn iterations. """ self.n = n self.k = k self.validation_set_efba = ChallengeResponseSet( challenges=LTFArray.efba_bit( LTFArray.transform_lightweight_secure( validation_set.challenges, k)), responses=validation_set.responses) self.logger = logger self.lr_learner = LogisticRegression( t_set=training_set, n=n, k=k, transformation=LTFArray.transform_lightweight_secure, combiner=LTFArray.combiner_xor, weights_mu=weights_mu, weights_sigma=weights_sigma, weights_prng=weights_prng, logger=logger, iteration_limit=lr_iteration_limit, minibatch_size=mini_batch_size, convergence_decimals=convergence_decimals, shuffle=shuffle) self.initial_accuracy = .5 self.initial_lr_iterations = 0 self.initial_model = None self.total_lr_iterations = 0 self.best_permutation_iteration = 0 self.total_permutation_iterations = 0 self.best_permutation = None self.best_accuracy = None assert n in ( 64, 128 ), 'Correlation attack for %i bit is currently not supported.' % n assert validation_set.N >= 1000, 'Validation set should contain at least 1000 challenges.' self.correlation_permutations = loadmat( 'data/correlation_permutations_lightweight_secure_%i_10.mat' % n)['shiftOverviewData'][:, :, 0].astype('int64') def learn(self): """ Compute a model according to the given LTF Array parameters and training set. Note that this function can take long to return. :return: pypuf.simulation.arbiter_based.LTFArray The computed model. """ self.initial_model = initial_model = self.lr_learner.learn() self.logger.debug('initial weights for corr attack:') self.logger.debug(','.join( map(str, initial_model.weight_array.flatten()))) self.initial_accuracy = self.approx_accuracy( initial_model, self.validation_set_efba.block_subset(0, 2)) self.initial_lr_iterations = self.lr_learner.iteration_count self.total_lr_iterations = self.initial_lr_iterations initial_updater = self.lr_learner.updater self.best_accuracy = self.initial_accuracy self.logger.debug('Initial accuracy is %.4f' % self.initial_accuracy) if self.initial_accuracy < self.OPTIMIZATION_ACCURACY_LOWER_BOUND: self.logger.debug('initial learning below threshold, aborting') return initial_model if self.initial_accuracy > self.OPTIMIZATION_ACCURACY_GOAL: self.logger.debug('initial learning successful, aborting') return initial_model # Try all permutations with high initial accuracy and see if any of them lead to a good final result high_accuracy_permutations = self.find_high_accuracy_weight_permutations( initial_model.weight_array, # allow some accuracy loss by permuting # the higher the initial accuracy, the higher the loss we allow # result will never be below 0.925 1.2 * self.best_accuracy - .2) best_model = initial_model self.logger.debug('Trying %i permuted weights.' % len(high_accuracy_permutations)) for (iteration, perm_data) in enumerate(high_accuracy_permutations): self.total_permutation_iterations += 1 weights = self.adopt_weights(initial_model.weight_array, perm_data.permutation) self.lr_learner.updater = deepcopy(initial_updater) self.lr_learner.updater.step_size *= 10 model = self.lr_learner.learn(init_weight_array=weights, refresh_updater=False) self.total_lr_iterations += self.lr_learner.iteration_count accuracy = self.approx_accuracy( model, self.validation_set_efba.block_subset(1, 2)) self.logger.debug( 'With permutation no %d=%s, after restarting the learning we achieved accuracy %.4f -> %.4f!' % (iteration, perm_data.permutation, perm_data.accuracy, accuracy)) if accuracy > 0.1 + 0.9 * self.initial_accuracy \ and accuracy > self.best_accuracy: # demand some "substantial" improvement of accuracy # what substantial means becomes weaker as we approach # perfect accuracy best_model = model self.best_accuracy = accuracy self.best_permutation_iteration = iteration + 1 self.best_permutation = perm_data.permutation else: self.logger.debug( 'Learning after permuting lead to accuracy %.2f, no improvement :-(' % accuracy) if accuracy > self.OPTIMIZATION_ACCURACY_GOAL: self.logger.debug( 'Found a model with accuracy better than %.2f. Terminating' % self.OPTIMIZATION_ACCURACY_GOAL) return model self.logger.debug( 'After trying all permutations, we found a model with acc. %.2f.' % self.best_accuracy) return best_model def find_high_accuracy_weight_permutations(self, weights, threshold): """ Gives permutations for the weight-array resulting in the highest model accuracies. :param weights: The original weight-array :param threshold: Minimum accuracy to consider :return: The 5k permutations with the highest accuracy """ high_accuracy_permutations = [] adopted_instance = LTFArray( weight_array=zeros((self.k, self.n)), transform=LTFArray.transform_lightweight_secure, combiner=LTFArray.combiner_xor) for permutation in list(permutations(range(self.k)))[1:]: adopted_instance.weight_array = self.adopt_weights( weights, permutation) accuracy = self.approx_accuracy(adopted_instance) self.logger.debug('For permutation %s, we have accuracy %.4f' % (permutation, accuracy)) if accuracy >= threshold: high_accuracy_permutations.append( PermData(permutation, accuracy)) high_accuracy_permutations.sort(key=lambda x: -x.accuracy) return high_accuracy_permutations[:5 * self.k] def approx_accuracy(self, instance, efba_set=None): """ Approximate the accuracy of the instance on the given set. :param instance: pypuf.simulation.arbiter_based.LTFArray :param efba_set: A challenge-response-set containing efba sub-challenges (default: self.validation_set_efba) :return: Accuracy of the instance """ if efba_set is None: efba_set = self.validation_set_efba size = efba_set.N responses = sign( instance.combiner(instance.core_eval(efba_set.challenges))) return count_nonzero(responses == efba_set.responses) / size def adopt_weights(self, weights, permutation): """ Adopts the weights with the given permutation exploiting the correlations of the lightweight-secure transform. :param weights: A weight-array of an LTFArray :param permutation: Permutation as returned from itertools.permutations :return: Permuted weight-array """ adopted_weights = empty(weights.shape) for l in range(self.k): adopted_weights[permutation[l], :] = \ roll(weights[l, :], self.correlation_permutations[l, permutation[l]]) return adopted_weights
) train_set = tools.TrainingSet(instance=instance, N=15000) val_set = train_set.subset(slice(10000, None)) train_set = train_set.subset(slice(None, 10000)) challenges, responses = train_set.challenges, train_set.responses print(challenges.shape, responses.shape) from pypuf.learner.regression.logistic_regression import LogisticRegression lr_learner = LogisticRegression( t_set=train_set, n=64, k=2, transformation=LTFArray.transform_atf, combiner=LTFArray.combiner_xor, ) model = lr_learner.learn() val_set_predicted_responses = model.eval(val_set.challenges) acc = accuracy_score(val_set_predicted_responses, val_set.responses) print('accuracy: ', acc) from pypuf.learner.other import Boosting boosting_learner = Boosting( t_set=train_set,
class ExperimentLogisticRegression(Experiment): """ This Experiment uses the logistic regression learner on an LTFArray PUF simulation. """ def __init__(self, progress_log_prefix, parameters): progress_log_name = None if progress_log_prefix is None else '%s.0x%x_0x%x_0_%i_%i_%i_%s_%s' % ( progress_log_prefix, parameters.seed_model, parameters.seed_instance, parameters.n, parameters.k, parameters.N, parameters.transformation, parameters.combiner, ) super().__init__(progress_log_name, parameters) self.instance = None self.learner = None self.model = None def prepare(self): """ Initializes the instance, the training set and the learner to then run the logistic regression with the given parameters. """ self.instance = LTFArray( weight_array=LTFArray.normal_weights( self.parameters.n, self.parameters.k, random_instance=RandomState( seed=self.parameters.seed_instance)), transform=self.parameters.transformation, combiner=self.parameters.combiner, ) self.learner = LogisticRegression( tools.TrainingSet(instance=self.instance, N=self.parameters.N, random_instance=RandomState( self.parameters.seed_challenge)), self.parameters.n, self.parameters.k, transformation=self.instance.transform, combiner=self.instance.combiner, weights_prng=RandomState(seed=self.parameters.seed_model), logger=self.progress_logger, minibatch_size=self.parameters.mini_batch_size, convergance_decimals=self.parameters.convergence_decimals or 2, shuffle=self.parameters.shuffle, ) def run(self): """ Runs the learner """ self.model = self.learner.learn() def analyze(self): """ Analyzes the learned result. """ assert self.model is not None accuracy = 1.0 - tools.approx_dist( self.instance, self.model, min(10000, 2**self.parameters.n), random_instance=RandomState(self.parameters.seed_distance), ) return Result( experiment_id=self.id, pid=getpid(), iteration_count=self.learner.iteration_count, epoch_count=self.learner.epoch_count, gradient_step_count=self.learner.gradient_step_count, measured_time=self.measured_time, accuracy=accuracy, model=self.model.weight_array.flatten() / norm(self.model.weight_array.flatten()), )
if algorithm==2: learner = PolytopeAlgorithm( instance, tools.TrainingSet(instance=instance, N=N), n, k, transformation=transformation, combiner=combiner, weights_prng=model_prng, ) else: learner = LogisticRegression( tools.TrainingSet(instance=instance, N=N), n, k, transformation=transformation, combiner=combiner, weights_prng=model_prng, ) i = 0 dist = 1 while i < restarts and 1 - dist < convergence: stderr.write('\r%5i/%5i ' % (i+1, restarts if restarts < float('inf') else 0)) start = time.time() model = learner.learn() end = time.time() training_times = append(training_times, end - start) dist = tools.approx_dist(instance, model, min(10000, 2 ** n)) accuracy = append(accuracy, 1 - dist)
def __init__(self, n, k, training_set, validation_set, weights_mu=0, weights_sigma=1, weights_prng=RandomState(), lr_iteration_limit=1000, mini_batch_size=0, convergence_decimals=2, shuffle=False, logger=None): """ Initialize a Correlation Attack Learner for the specified LTF Array which uses transform_lightweight_secure. :param n: Input length :param k: Number of parallel LTFs in the LTF Array :param training_set: The training set, i.e. a data structure containing challenge response pairs :param validation_set: The validation set, i.e. a data structure containing challenge response pairs. Used for approximating accuracies of permuted models (can be smaller e.g. 0.1*training_set_size) :param weights_mu: mean of the Gaussian that is used to choose the initial model :param weights_sigma: standard deviation of the Gaussian that is used to choose the initial model :param weights_prng: PRNG to draw the initial model from. Defaults to fresh `numpy.random.RandomState` instance. :param lr_iteration_limit: Iteration limit for a single LR learner run :param logger: logging.Logger Logger which is used to log detailed information of learn iterations. """ self.n = n self.k = k self.validation_set_efba = ChallengeResponseSet( challenges=LTFArray.efba_bit( LTFArray.transform_lightweight_secure( validation_set.challenges, k)), responses=validation_set.responses) self.logger = logger self.lr_learner = LogisticRegression( t_set=training_set, n=n, k=k, transformation=LTFArray.transform_lightweight_secure, combiner=LTFArray.combiner_xor, weights_mu=weights_mu, weights_sigma=weights_sigma, weights_prng=weights_prng, logger=logger, iteration_limit=lr_iteration_limit, minibatch_size=mini_batch_size, convergence_decimals=convergence_decimals, shuffle=shuffle) self.initial_accuracy = .5 self.initial_lr_iterations = 0 self.initial_model = None self.total_lr_iterations = 0 self.best_permutation_iteration = 0 self.total_permutation_iterations = 0 self.best_permutation = None self.best_accuracy = None assert n in ( 64, 128 ), 'Correlation attack for %i bit is currently not supported.' % n assert validation_set.N >= 1000, 'Validation set should contain at least 1000 challenges.' self.correlation_permutations = loadmat( 'data/correlation_permutations_lightweight_secure_%i_10.mat' % n)['shiftOverviewData'][:, :, 0].astype('int64')
class ExperimentLogisticRegression(Experiment): """ This Experiment uses the logistic regression learner on an LTFArray PUF simulation. """ def __init__(self, log_name, n, k, N, seed_instance, seed_model, transformation, combiner): super().__init__(log_name='%s.0x%x_0x%x_0_%i_%i_%i_%s_%s' % ( log_name, seed_model, seed_instance, n, k, N, transformation.__name__, combiner.__name__, ), ) self.n = n self.k = k self.N = N self.seed_instance = seed_instance self.instance_prng = RandomState(seed=self.seed_instance) self.seed_model = seed_model self.model_prng = RandomState(seed=self.seed_model) self.combiner = combiner self.transformation = transformation self.instance = None self.learner = None self.model = None def run(self): """ Initializes the instance, the training set and the learner to then run the logistic regression with the given parameters. """ # TODO input transformation is computed twice. Add a shortcut to recycle results from the first computation self.instance = LTFArray( weight_array=LTFArray.normal_weights( self.n, self.k, random_instance=self.instance_prng), transform=self.transformation, combiner=self.combiner, ) self.learner = LogisticRegression( tools.TrainingSet(instance=self.instance, N=self.N), self.n, self.k, transformation=self.transformation, combiner=self.combiner, weights_prng=self.model_prng, logger=self.progress_logger, ) self.model = self.learner.learn() def analyze(self): """ Analyzes the learned result. """ assert self.model is not None self.result_logger.info( # seed_instance seed_model i n k N trans comb iter time accuracy model values '0x%x\t' '0x%x\t' '%i\t' '%i\t' '%i\t' '%i\t' '%s\t' '%s\t' '%i\t' '%f\t' '%f\t' '%s' % ( self.seed_instance, self.seed_model, 0, # restart count, kept for compatibility to old log files self.n, self.k, self.N, self.transformation.__name__, self.combiner.__name__, self.learner.iteration_count, self.measured_time, 1.0 - tools.approx_dist(self.instance, self.model, min(10000, 2**self.n)), ','.join( map( str, self.model.weight_array.flatten() / norm(self.model.weight_array.flatten())))))
class ExperimentLogisticRegression(Experiment): """ This Experiment uses the logistic regression learner on an LTFArray PUF simulation. """ def __init__( self, log_name, n, k, N, seed_instance, seed_model, transformation, combiner, seed_challenge=0x5A551, seed_chl_distance=0xB055, ): """ :param log_name: string Prefix of the path or name of the experiment log file. :param n: int Number of stages of the PUF :param k: int Number different LTFArrays :param N: int Number of challenges which are generated in order to learn the PUF simulation. :param seed_instance: int The seed which is used to initialize the pseudo-random number generator which is used to generate the stage weights for the arbiter PUF simulation. :param seed_model: int The seed which is used to initialize the pseudo-random number generator which is used to generate the stage weights for the learner arbiter PUF simulation. :param transformation: A function: array of int with shape(N,k,n), int number of PUFs k -> shape(N,k,n) The function transforms input challenges in order to increase resistance against attacks. :param combiner: A function: array of int with shape(N,k,n) -> array of in with shape(N) The functions combines the outputs of k PUFs to one bit results, in oder to increase resistance against attacks. :param seed_challenge: int default is 0x5A551 The seed which is used to initialize the pseudo-random number generator which is used to draft challenges for the TrainingSet. :param seed_chl_distance: int default is 0xB055 The seed which is used to initialize the pseudo-random number generator which is used to draft challenges for the accuracy calculation. """ super().__init__( log_name='%s.0x%x_0x%x_0_%i_%i_%i_%s_%s' % ( log_name, seed_model, seed_instance, n, k, N, transformation.__name__, combiner.__name__, ), ) self.n = n self.k = k self.N = N self.seed_instance = seed_instance self.instance_prng = RandomState(seed=self.seed_instance) self.seed_model = seed_model self.model_prng = RandomState(seed=self.seed_model) self.combiner = combiner self.transformation = transformation self.seed_challenge = seed_challenge self.challenge_prng = RandomState(self.seed_challenge) self.seed_chl_distance = seed_chl_distance self.distance_prng = RandomState(self.seed_chl_distance) self.instance = None self.learner = None self.model = None def run(self): """ Initializes the instance, the training set and the learner to then run the logistic regression with the given parameters. """ # TODO input transformation is computed twice. Add a shortcut to recycle results from the first computation self.instance = LTFArray( weight_array=LTFArray.normal_weights(self.n, self.k, random_instance=self.instance_prng), transform=self.transformation, combiner=self.combiner, ) self.learner = LogisticRegression( tools.TrainingSet(instance=self.instance, N=self.N, random_instance=self.challenge_prng), self.n, self.k, transformation=self.transformation, combiner=self.combiner, weights_prng=self.model_prng, logger=self.progress_logger, ) self.model = self.learner.learn() def analyze(self): """ Analyzes the learned result. """ assert self.model is not None self.result_logger.info( # seed_instance seed_model i n k N trans comb iter time accuracy model values '0x%x\t' '0x%x\t' '%i\t' '%i\t' '%i\t' '%i\t' '%s\t' '%s\t' '%i\t' '%f\t' '%f\t' '%s', self.seed_instance, self.seed_model, 0, # restart count, kept for compatibility to old log files self.n, self.k, self.N, self.transformation.__name__, self.combiner.__name__, self.learner.iteration_count, self.measured_time, 1.0 - tools.approx_dist( self.instance, self.model, min(10000, 2 ** self.n), random_instance=self.distance_prng, ), ','.join(map(str, self.model.weight_array.flatten() / norm(self.model.weight_array.flatten()))) )
def main(args): if len(args) != 10: stderr.write('LTF Array Simulator and Logistic Regression Learner\n') stderr.write('Usage:\n') stderr.write( 'sim_learn.py n k transformation combiner N restarts seed_instance seed_model\n' ) stderr.write(' n: number of bits per Arbiter chain\n') stderr.write(' k: number of Arbiter chains\n') stderr.write( ' transformation: used to transform input before it is used in LTFs\n' ) stderr.write(' currently available:\n') stderr.write(' - id -- does nothing at all\n') stderr.write( ' - atf -- convert according to "natural" Arbiter chain\n' ) stderr.write(' implementation\n') stderr.write( ' - mm -- designed to achieve maximum PTF expansion length\n' ) stderr.write( ' only implemented for k=2 and even n\n') stderr.write( ' - lightweight_secure -- design by Majzoobi et al. 2008\n' ) stderr.write( ' only implemented for even n\n' ) stderr.write( ' - shift_lightweight_secure -- design like Majzoobi\n' ) stderr.write( ' et al. 2008, but with the shift\n' ) stderr.write( ' operation executed first\n' ) stderr.write( ' only implemented for even n\n' ) stderr.write( ' - soelter_lightweight_secure -- design like Majzoobi\n' ) stderr.write( ' et al. 2008, but one bit different\n' ) stderr.write( ' only implemented for even n\n' ) stderr.write( ' - 1_n_bent -- one LTF gets "bent" input, the others id\n' ) stderr.write( ' - 1_1_bent -- one bit gets "bent" input, the others id,\n' ) stderr.write( ' this is proven to have maximum PTF\n' ) stderr.write(' length for the model\n') stderr.write( ' - polynomial -- challenges are interpreted as polynomials\n' ) stderr.write( ' from GF(2^64). From the initial challenge c,\n' ) stderr.write( ' the i-th Arbiter chain gets the coefficients \n' ) stderr.write( ' of the polynomial c^(i+1) as challenge.\n' ) stderr.write( ' For now only challenges with length n=64 are accepted.\n' ) stderr.write( ' - permutation_atf -- for each Arbiter chain first a pseudorandom permutation \n' ) stderr.write( ' is applied and thereafter the ATF transform.\n' ) stderr.write( ' - random -- Each Arbiter chain gets a random challenge derived from the\n' ) stderr.write( ' original challenge using a PRNG.\n') stderr.write( ' combiner: used to combine the output bits to a single bit\n' ) stderr.write(' currently available:\n') stderr.write( ' - xor -- output the parity of all output bits\n' ) stderr.write( ' - ip_mod2 -- output the inner product mod 2 of all output\n' ) stderr.write(' bits (even n only)\n') stderr.write( ' N: number of challenge response pairs in the training set\n' ) stderr.write( ' restarts: number of repeated initializations the learner\n' ) stderr.write( ' use float number x, 0<x<1 to repeat until given accuracy\n' ) stderr.write( ' instances: number of repeated initializations the instance\n' ) stderr.write( ' The number total learning attempts is restarts*instances.\n' ) stderr.write( ' seed_instance: random seed used for LTF array instance\n') stderr.write( ' seed_model: random seed used for the model in first learning attempt\n' ) quit(1) n = int(args[1]) k = int(args[2]) transformation_name = args[3] combiner_name = args[4] N = int(args[5]) if float(args[6]) < 1: restarts = float('inf') convergence = float(args[6]) else: restarts = int(args[6]) convergence = 1.1 instances = int(args[7]) seed_instance = int(args[8], 16) seed_model = int(args[9], 16) # reproduce 'random' numbers and avoid interference with other random numbers drawn instance_prng = RandomState(seed=seed_instance) model_prng = RandomState(seed=seed_model) transformation = None combiner = None try: transformation = getattr(LTFArray, 'transform_%s' % transformation_name) except AttributeError: stderr.write( 'Transformation %s unknown or currently not implemented\n' % transformation_name) quit() try: combiner = getattr(LTFArray, 'combiner_%s' % combiner_name) except AttributeError: stderr.write('Combiner %s unknown or currently not implemented\n' % combiner_name) quit() stderr.write( 'Learning %s-bit %s XOR Arbiter PUF with %s CRPs and %s restarts.\n\n' % (n, k, N, restarts)) stderr.write('Using\n') stderr.write(' transformation: %s\n' % transformation) stderr.write(' combiner: %s\n' % combiner) stderr.write(' instance random seed: 0x%x\n' % seed_instance) stderr.write(' model random seed: 0x%x\n' % seed_model) stderr.write('\n') accuracy = array([]) training_times = array([]) iterations = array([]) for j in range(instances): stderr.write('----------- Choosing new instance. ---------\n') instance = LTFArray( weight_array=LTFArray.normal_weights( n, k, random_instance=instance_prng), transform=transformation, combiner=combiner, ) lr_learner = LogisticRegression( tools.TrainingSet(instance=instance, N=N), n, k, transformation=transformation, combiner=combiner, weights_prng=model_prng, ) i = 0 dist = 1 while i < restarts and 1 - dist < convergence: stderr.write('\r%5i/%5i ' % (i + 1, restarts if restarts < float('inf') else 0)) start = time.time() model = lr_learner.learn() end = time.time() training_times = append(training_times, end - start) dist = tools.approx_dist(instance, model, min(10000, 2**n)) accuracy = append(accuracy, 1 - dist) iterations = append(iterations, lr_learner.iteration_count) # output test result in machine-friendly format # seed_ltf seed_model idx_restart n k N transformation combiner iteration_count time accuracy stderr.write(' '.join([ '0x%x' % seed_instance, '0x%x' % seed_model, '%5d' % i, '%3d' % n, '%2d' % k, '%6d' % N, '%s' % transformation_name, '%s' % combiner_name, '%4d' % lr_learner.iteration_count, '%9.3f' % (end - start), '%1.5f' % (1 - dist), ]) + '\n') #stderr.write('training time: % 5.3fs' % (end - start)) #stderr.write('min training distance: % 5.3f' % lr_learner.min_distance) #stderr.write('test distance (1000 samples): % 5.3f\n' % dist) i += 1 stderr.write('\r \r') stderr.write('\n\n') stderr.write('training times: %s\n' % training_times) stderr.write('iterations: %s\n' % iterations) stderr.write('test accuracy: %s\n' % accuracy) stderr.write('\n\n') stderr.write( 'min/avg/max training time : % 9.3fs /% 9.3fs /% 9.3fs\n' % (amin(training_times), mean(training_times), amax(training_times))) stderr.write('min/avg/max iteration count: % 9.3f /% 9.3f /% 9.3f \n' % (amin(iterations), mean(iterations), amax(iterations))) stderr.write('min/avg/max test accuracy : % 9.3f /% 9.3f /% 9.3f \n' % (amin(accuracy), mean(accuracy), amax(accuracy))) stderr.write('\n\n')