Skip to content

Commit

Permalink
adds Beli PUF simulation, attack, and docs
Browse files Browse the repository at this point in the history
  • Loading branch information
nils-wisiol committed Jan 14, 2022
1 parent 337db80 commit 73a4de9
Show file tree
Hide file tree
Showing 10 changed files with 558 additions and 53 deletions.
2 changes: 2 additions & 0 deletions docs/appendix/bibliography.rst
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ Bibliography
.. [AM21] Aghaie, A. & Moradi, A. Inconsistency of Simulation and Practice in Delay-based Strong PUFs. IACR
Transactions on Cryptographic Hardware and Embedded Systems 520–551 (2021) doi:10.46586/tches.v2021.i3.520-551.
.. [AMTW21] Aghaie, A. & Moradi, A. & Tobisch, J. & Wisiol, N. Generalization of Arbiter PUF -- New Constructions and
Novel Analyses. Unpublished (2021).
.. [AZ17] Alkatheiri, M. S. & Zhuang, Y. Towards fast and accurate machine learning attacks of feed-forward arbiter
PUFs. in 2017 IEEE Conference on Dependable and Secure Computing 181–187 (2017). doi:10.1109/DESEC.2017.8073845.
.. [AZA18] Aseeri, A. O., Zhuang, Y. & Alkatheiri, M. S. A Machine Learning-Based Security Vulnerability Study on XOR
Expand Down
71 changes: 22 additions & 49 deletions docs/attacks/beli.rst
Original file line number Diff line number Diff line change
@@ -1,73 +1,46 @@
Logistic Regression Attack
==========================
Beli PUF Attacks
================

The Logistic Regression Attack (LR Attack) was introduced by Rührmair et al. [RSSD10]_ to model XOR Arbiter PUFs.
In pypuf, an modernized and optimized version of the LR attack is implemented using tensorflow. The
`original implementation <http://www.pcp.in.tum.de/code/lr.zip>`_ is also available from the authors. A study of the
data complexity of the LR attack using a different unpublished implementation of the LR attack was done by Tobisch and
Becker [TB15]_.
[AMTW21]_

Example Usage
-------------

To run the attack, CRP data of the PUF token under attack is required. Such data can be obtained through experiments
on real hardware, or using a simulation. In this example, we use the pypuf XOR Arbiter PUF simulator:
Logistic-Regression-Based
-------------------------

>>> import pypuf.simulation, pypuf.io
>>> puf = pypuf.simulation.XORArbiterPUF(n=64, k=4, seed=1)
>>> crps = pypuf.io.ChallengeResponseSet.from_simulation(puf, N=50000, seed=2)
>>> puf = pypuf.simulation.TwoBitBeliPUF(n=64, k=1, seed=1)
>>> crps = pypuf.io.ChallengeResponseSet.from_simulation(puf, N=10000, seed=2)


To run the attack, we configure the attack object with the challenge response data and attack parameters. The parameters
need careful adjustment for each choice of security parameters in the PUF. Then the attack is run using the
:meth:`pypuf.attack.LRAttack2021.fit` method.

>>> import pypuf.attack
>>> attack = pypuf.attack.LRAttack2021(crps, seed=3, k=4, bs=1000, lr=.001, epochs=100)
>>> attack = pypuf.attack.TwoBitBeliLR(crps, seed=3, k=1, bs=256, lr=1, epochs=15)
>>> attack.fit() # doctest:+ELLIPSIS +NORMALIZE_WHITESPACE
Epoch 1/100
...
50/50 [==============================] - ... - loss: 0.4... - accuracy: 0.9... - val_loss: 0.4643 - val_accuracy: 0.9620
<pypuf.simulation.base.LTFArray object at 0x...>
Epoch 1/15
...
36/36 [==============================] ... loss: 0.2... - accuracy: 0.9...
<pypuf.simulation.delay.TwoBitBeliPUF object at 0x...>
>>> model = attack.model

The model accuracy can be measured using the pypuf accuracy metric :meth:`pypuf.metrics.accuracy`.

>>> import pypuf.metrics
>>> pypuf.metrics.similarity(puf, model, seed=4)
array([0.966])

Applicability
-------------
array([0.953, 0.944])

The Logistic Regression can be extended to other variants of the XOR Arbiter PUF [WBMS19]_. This functionality is
currently not yet included in pypuf, so that the user needs to perform the appropriate conversions before using
:class:`pypuf.attack.LRAttack2021`.

This implementation is also suitable to conduct the splitting attack on the Interpose PUF [WMPN19]_.

API
---

.. autoclass:: pypuf.attack.LRAttack2021
:members: __init__, fit, model, history
.. autoclass:: pypuf.attack.TwoBitBeliLR
:members: __init__, fit, beli_output, model, history

.. autoclass:: pypuf.attack.OneBitBeliLR
:members: __init__, fit, beli_output, model, history

Performance
-----------

The pypuf implementation is tested using tensorflow 2.4 on Intel Xeon E5-2630 v4 attacking :math:`n`-bit :math:`k`-XOR
Arbiter PUFs. The results below are compared with those of Tobisch and Becker [TB15]_, which have been obtained in 2015
using up to 16 cores.

=== ==== ===== ============ =========== ======= ==================
n k CRPs success rate duration cores [TB15]_ / 16 cores
=== ==== ===== ============ =========== ======= ==================
64 4 30k 10/10 <1 min 4 <1 min
64 5 260k 10/10 4 min 4 <1 min
64 6 2M 20/20 <1 min 4 1 min
64 7 20M 10/10 3 min 4 55 min
64 8 150M 10/10 28 min 4 391 min
64 9 350M 2266 min
64 9 500M 7/10 14 min 40
64 10 1B 6/10 41 min 40
=== ==== ===== ============ =========== ======= ==================
=== ==== ===== ============ =========== =======
n k CRPs success rate duration cores
=== ==== ===== ============ =========== =======
=== ==== ===== ============ =========== =======
2 changes: 2 additions & 0 deletions docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ Some functionality is only implemented in the
Overview <simulation/overview>
simulation/arbiter_puf
simulation/delay
simulation/beli
simulation/bistable
simulation/optical
simulation/base
Expand Down Expand Up @@ -47,6 +48,7 @@ Some functionality is only implemented in the

Overview <attacks/overview>
Logistic Regression <attacks/lr>
Beli PUF <attacks/beli>
Multilayer Perceptron <attacks/mlp>
LMN (PUFMeter) <attacks/lmn>
attacks/linear_regression
Expand Down
54 changes: 54 additions & 0 deletions docs/simulation/beli.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
Beli PUF and XOR Beli PUF
=========================

Intro to Beli PUF, Motvation, Design, blah [AMTW21]_.

To simulate a basic Beli PUF with two output bits, use

>>> import pypuf.simulation, pypuf.io
>>> puf = pypuf.simulation.TwoBitBeliPUF(n=32, k=1, seed=1)
>>> crps = pypuf.io.random_inputs(n=32, N=3, seed=2)
>>> puf.eval(crps)
array([[-1, -1],
[ 1, 1],
[-1, -1]])

Beli PUF can also output the index of the delay line with the fastest signal for a given challenge,

>>> puf = pypuf.simulation.BeliPUF(n=32, k=1, seed=1)
>>> puf.eval(crps)
array([[3],
[0],
[3]])

Observe that the two Beli PUF instances above use the same internal delays and differ only in the output format
specification.

:class:`OneBitBeliPUF` is a Beli PUF version which returns the XOR value of the :class:`TwoBitBeliPUF` version shown
above,

>>> puf = pypuf.simulation.OneBitBeliPUF(n=32, k=1, seed=1)
>>> puf.eval(crps)
array([1, 1, 1])

All Beli PUFs shown above can be arranged into an XOR Beli PUF by using the `k` parameter when instantiating:

>>> puf = pypuf.simulation.OneBitBeliPUF(n=32, k=4, seed=1)
>>> puf.eval(crps)
array([-1, 1, -1])

.. note::
pypuf currently does not implement noisy Beli PUF simulation.
However, it ships CRP data of a Beli PUF implemented in FPGA.
TODO add link.


API
---

.. autoclass:: pypuf.simulation.BeliPUF
:members: __init__, challenge_length, response_length, eval, val, signal_path, features

.. autoclass:: pypuf.simulation.TwoBitBeliPUF

.. autoclass:: pypuf.simulation.OneBitBeliPUF
2 changes: 1 addition & 1 deletion pypuf/attack/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from .lr2021 import LRAttack2021
from .lr2021 import LRAttack2021, TwoBitBeliLR, OneBitBeliLR

from .mlp2021 import MLPAttack2021

Expand Down
131 changes: 129 additions & 2 deletions pypuf/attack/lr2021.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
from typing import Optional
from typing import Optional, List

import numpy as np
import tensorflow as tf

from .base import OfflineAttack
from ..io import ChallengeResponseSet
from ..simulation import XORArbiterPUF
from ..simulation import XORArbiterPUF, BeliPUF, TwoBitBeliPUF, OneBitBeliPUF
from ..simulation.base import Simulation, LTFArray


Expand Down Expand Up @@ -175,3 +175,130 @@ def keras_to_pypuf(self, keras_model: tf.keras.Model) -> LTFArray:
bias[l] = layer_weights[1]

return LTFArray(weight_array=weights, bias=bias, transform=XORArbiterPUF.transform_atf)


class BeliLR(LRAttack2021):

model_class = None

@staticmethod
def beli_output(output_delays: List[tf.Tensor]) -> List[tf.Tensor]:
raise NotImplementedError

def beli_model(self, input_tensor: tf.Tensor) -> List[tf.Tensor]:
internal_delays = tf.keras.layers.Dense(
units=1,
use_bias=False,
kernel_initializer=tf.keras.initializers.RandomNormal(mean=10, stddev=.05),
activation=None,
)
output_delays = [internal_delays(input_tensor[:, i]) for i in range(input_tensor.shape[1])]
return self.beli_output(output_delays)

def fit(self, verbose: bool = True) -> Simulation:
"""
Using tensorflow, runs the attack as configured and returns the obtained model.
:param verbose: If true (the default), tensorflow will write progress information to stdout.
:return: Model of the Beli PUF under attack.
"""
tf.random.set_seed(self.seed)

N = self.crps.challenges.shape[0]
n = self.crps.challenge_length
k = self.k

input_tensor = tf.keras.Input(shape=(4, 4 * n))
beli_models = tf.transpose(
[self.beli_model(input_tensor) for _ in range(k)], # by list comprehension, k-axis is axis 0
(2, 1, 0, 3) # swap k-axis to axis 2 and keep sample axis at axis 0
) # output dim: (sample, m, k, 1)
xor = tf.math.reduce_prod(beli_models, axis=2) # xor along k-axis
outputs = tf.keras.layers.Activation(tf.keras.activations.tanh)(xor)

model = tf.keras.Model(inputs=input_tensor, outputs=outputs)
model.compile(
optimizer=tf.keras.optimizers.Adadelta(learning_rate=self.lr),
loss=self.loss,
metrics=[self.accuracy],
)
self._keras_model = model

self._history = model.fit(
BeliPUF.features(self.crps.challenges),
self.crps.responses,
batch_size=self.bs,
epochs=self.epochs,
validation_split=self.validation_split,
callbacks=[self.AccuracyStop(self.stop_validation_accuracy)],
verbose=verbose,
).history

self._model = self.keras_to_pypuf(model)
return self.model

def keras_to_pypuf(self, keras_model: tf.keras.Model) -> LTFArray:
"""
Given a Keras model that resulted from the attack of the :meth:`fit` method, constructs an
:class:`pypuf.simulation.BeliPUF` that computes the same model.
"""
delays = np.array([
layer.get_weights()[0].squeeze().reshape((8, -1))
for layer in keras_model.layers
if isinstance(layer, tf.keras.layers.Dense)]
)

k = delays.shape[0]
n = delays.shape[2] * 2

pypuf_model = self.model_class(n=n, k=k, seed=0)
pypuf_model.delays = delays

return pypuf_model


class TwoBitBeliLR(BeliLR):
model_class = TwoBitBeliPUF

@staticmethod
def beli_output(output_delays: List[tf.Tensor]) -> List[tf.Tensor]:
r"""
Returns a continuous estimate of the two output bits.
Let :math:`d_i` be the delay on delay line :math:`i`.
For the first bit, :math:`\min\{d_2,d_3\} - \min\{d_0,d_1\}` is returned.
This expression is positive if and only if :math:`d_0` or :math:`d_1` is the fastest signal.
As first response bit is positive if delay :math:`d_0` or :math:`d_1` is fastest,
this expression is an approximation of the first response bit value.
A similar argument holds for the second response bit.
"""
Min = tf.keras.layers.Minimum
d = output_delays
return [
Min()((d[2], d[3])) - Min()((d[0], d[1])),
Min()((d[1], d[3])) - Min()((d[0], d[2])),
]


class OneBitBeliLR(BeliLR):
model_class = OneBitBeliPUF

@staticmethod
def beli_output(output_delays: List[tf.Tensor]) -> List[tf.Tensor]:
r"""
Returns a continuous estimate of the output bit.
Let :math:`d_i` be the delay on delay line :math:`i`.
:math:`\min\{d_1,d_2\} - \min\{d_0,d_3\}` is returned.
This expression is positive if and only if :math:`d_0` or :math:`d_3` is the fastest signal.
As the response bit is positive if and only if the delay :math:`d_0` or :math:`d_3` is fastest,
this expression is an approximation of the response bit value.
"""
Min = tf.keras.layers.Minimum
d = output_delays
return [
Min()((d[1], d[2])) - Min()((d[0], d[3])),
]
2 changes: 1 addition & 1 deletion pypuf/simulation/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,6 @@
from .bistable import XORBistableRingPUF, BistableRingPUF

from .delay import XORArbiterPUF, XORFeedForwardArbiterPUF, ArbiterPUF, LightweightSecurePUF, RandomTransformationPUF, \
PermutationPUF, InterposePUF, FeedForwardArbiterPUF
PermutationPUF, InterposePUF, FeedForwardArbiterPUF, BeliPUF, OneBitBeliPUF, TwoBitBeliPUF

from .optical import IntegratedOpticalPUF
Loading

0 comments on commit 73a4de9

Please sign in to comment.