Physically Unclonable Functions (PUFs) are of research interest in the field of lightweight and secure authentication. PUFs usually provide a challenge-response-interface. It is of fundamental importance to study the security of this interface. One way to study the security is per empirical study. (Remember though, that this can only prove insecurity of any given PUF.)
pypuf provides simulations and attacks on PUFs; it aims at helping to understand PUFs and attacks on them. It also provides some tools for running experiments with the simulations and attacks.
Technically, pypuf heavily relies on numpy. Some operations base on an additional module that has been written in C (see installation section below).
Currently pypuf relies heavily on numpy
and is tested for Python versions 3.6 and 3.7.
Some operations also require the polymath package and/or the scipy package.
The recommended way to run pypuf is to use a virtual environment.
It can be created using the following steps.
Please make sure that virtualenv, python, the python development files,
and a compiler toolchain are available on your system.
(On Debian: apt install build-essential python3 python3-dev virtualenv
.)
In your pypuf clone directory,
# create and enter virtual environment
virtualenv -p python3 env
source env/bin/activate
# upgrade pip
python3 -m pip install --upgrade pip
# install requirements (polymath needs c99)
CC="gcc -std=c99" pip3 install -r requirements.txt
Afterwards, confirm a correct setup by running the tests:
python3 -m unittest
If you encounter any trouble, please refer to our continuous integration at travis-ci to see a working example or raise an issue on GitHub.
If you don't want to install any packages onto your system, you can use a docker container to run pypuf. To do so, please first install docker and docker-compose. Afterwards, pypuf studies can be run like this:
docker-compose run pypuf python3 -m study ltf_benchmark
You can run pypuf installing numpy and scipy from your distribution's repository.
This will prevent you from using any features that rely on polymath
and is hence not recommended.
It is an easier way however to get started quickly.
After installing python3
, numpy
, and scipy
run the example to make sure everything is setup okay.
(Unit tests for features relying on polymath
will fail in this scenario!)
pypuf mainly consists of three parts, simulation, learning and experimenting that will be elaborated below.
The simulation
module can simulate different types of Physically Unclonable Functions, currently mostly Arbiter-based
constructions. The learner
module contains a logistic regression learner that can be used for many attacks
on Arbiter-based PUFs and some other learners. Finally, the experimenting part of pypuf contains the experiments
module and the studies
module that can run experiments and aggregate them into results, respectively.
Note that pypuf mostly uses the {-1,1} notation of bits, where True = -1 and False = +1 (that is, -1 corresponds to traditional "1" and +1 corresponds to traditional "0").
The simulation currently consists of a very broad class, the LTF Array Simulator.
It can simulate an array of Linear Threshold Functions and hence simulate Arbiter PUFs,
XOR Arbiter PUFs, Lightweight Secure PUFs,
Majority Vote PUFs, and more custom designs.
To that end, the input transformation can be chosen (e.g. as designed for the Lightweight Secure PUF)
and the combiner function can be chosen (to generalize the usually used XOR function).
Another component of the simulation is the Fourier expansion of a Boolean function.
It either can be evaluated returning a real value or boxed into the sign operator, returning -1 or +1.
LTFArray
currently defines a couple of input transformations.
More input transformations can be added by implementing a function and provide the function as a constructor argument to LTFArray
.
id
: use the generated challenge directly as input to the LTF (note that this does not correspond to the physical implementation of Arbiter PUFs)atf
: use Arbiter Threshold Functions, that is, transform the challenges in a way such that we simulate physical implementations of Arbiter PUFslightweight_secure_original
: input transformation as defined by Majzoobi et al. in Lightweight Secure PUFssoelter_lightweight_secure
: as defined by Majzoobi et al., but with a one-bit modification due to Sölter.polynomial
: challenges are interpreted as polynomials from GF(2^n). From the initial challenge c the i-th Arbiter chain gets the coefficients of the polynomial c^(i+1) as challenge. Only challenges with length 8, 16, 24, 32, 48, 64 are accepted.permutation_atf
: for each Arbiter chain first a pseudorandom permutation is applied and thereafter the ATF transform.random
: Each Arbiter chain gets a random challenge derived from the original challenge using a PRNG.
LTFArray
also implements "input transformation generators" that can be used to combine existing input transformations into new ones.
generate_concatenated_transform(transform_1, nn, transform_2)
: the firstnn
bit will be transformed usingtransform_1
, the rest will be transformed withtransform_2
.generate_random_permutation_transform(seed, nn, kk, atf)
: each of thekk
LTFs will be fed a random, but fixed permutation generated based on the given seed. Ifatf
, then challenges will be ATF-transformed after permuting them.generate_stacked_transform(transform_1, kk, transform_2)
: the firstkk
challenges will be transformed usingtransform_1
, the rest will be transformed withtransform_2
.
LTFArray
currently provides the traditional XOR (that is, parity) as a combiner function,
as well as the Inner Product Mod 2 function.
Further combiner functions can be implemented as static functions in LTFArray
,
or anywhere else and given to the LTFArray
constructor.
pypuf currently ships a logistic regression algorithm that was proposed to learn (XOR) Arbiter PUFs by Sölter and Rührmair et al, utilizing the RPROP backpropagation. Additionally pypuf aims for the provision of PAC learning algorithms, currently represented only by the Low Degree Algorithm introduced by Mansour.
pypuf provides the experiments
module to run experiments in a reproducible and convenient way. The module contains the
the Experimenter
, a manager class that can run a list of experiments in parallel, maintain the experiments' status and
results and output the status of the experiment run. It is capable of storing results to disk and provides a callback
function that will be called after each finished experiment. Results and partial results are available as a pandas
DataFrame
for easy analysis. The module also contains the Experiment
base class that can be extended to create new
experiments.
The studies
module is designed to connect experiments with results. Each study inherits from the Study
base class
and defines a name, a list of experiments to be run and a way to analyze it. All studies can be run by calling the
study
module (defined in study.py
) and providing the package name of the study as the first argument. To run the
Logistic Regression Mini Batch study that analyzes the success rate of logistic regression attacks on XOR Arbiter PUFs
while using or not using mini batches, use
python3 -m study lr_minibatch.success_rates
The result data will be saved to ./results/success_rate_results.csv
(in pandas CSV format), the figures generated by
the study will be saved to ./figures/lr-minibatch*.pdf
.
To implement a study, please refer to the package documentation of studies.base
.
pypuf is primarily designed as an API. However, it provides a subset of its features as a command line interface.
sim_learn
is a command line interface that simulates an LTF array and tries to learn it using logistic regression.
For simulation and learning, a couple of parameters can be chosen on the command line.
Start sim_learn
without parameters to get detailed usage information.
mv_num_of_votes
is a command line interface that allows to compute the minimum number of required votes in a Majority Vote XOR Arbiter PUF
such that a certain stability is achieved.
For details, please refer to Why Attackers Lose.
stability_calculation
is a command line interface that allows to generate a stability histogram for a simulated PUF,
i.e. to determine an approximation of how the probability to see the 'correct' answer is distributed among the challenge space.
For details, please refer to Why Attackers Lose.
Example usage of sim_learn
that simulates a 64 bit 2-xor Arbiter PUF and learns it to approx. 98% from 12000 challenge response pairs:
python3 sim_learn.py 64 2 atf xor 12000 1 1 0xdead 0xbeef
pypuf's two most important interfaces are the Learner
and the Simulation
that can interact with each other.
This example creates a 64 bit 2-XOR XOR Arbiter PUF instance
and a Logistic Regression learner learner_lr
to learn it.
After learning, the model's accuracy is tested and the result is printed.
To run, use python3 example.py
.
from pypuf import tools
from pypuf.learner.regression.logistic_regression import LogisticRegression
from pypuf.simulation.arbiter_based.ltfarray import LTFArray
# create a simulation with random (Gaussian) weights
# for 64-bit 2-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)
All contributions receive warm welcome! To contribute, simply open an issue and/or pull request and maintain coding style. Please follow the coding standards within this project, that is, keep your code just like the code that is already there. Also, please don't forget to add tests for your code. We will only accept contributions under GNU GPLv3.
If you're using pypuf in your research, please let us know so we can link to your work here.
- Is your contribution GPLv3 compatible?
- Update README.md accordingly
- Document new code, update code comments for changed code
- Provide tests for your code (to run the tests locally, use
python3 -m unittest
) - Do not use
numpy.random
directly; always use annumpy.random.RandomState
instance.
Significant contribution to this project are due to (in chronological order):
- Nils Wisiol (nils.wisiol++a+t++fu-berlin.de)
- Christoph Graebnitz (christoph.graebnitz++a+t++fu-berlin.de)
- Christopher Mühl (chrism++a+t++zedat.fu-berlin.de)
- Benjamin Zengin (benjamin.zengin++a+t++fu-berlin.de)