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 20, 2022
1 parent 7ebe626 commit e473eb0
Show file tree
Hide file tree
Showing 10 changed files with 536 additions and 5 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
2 changes: 1 addition & 1 deletion docs/attacks/beli.rst
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ Logistic-Regression-Based
>>> attack.fit() # doctest:+ELLIPSIS +NORMALIZE_WHITESPACE
Epoch 1/15
...
36/36 [==============================] ... loss: 0.2... - accuracy: 0.9...
39/39 [==============================] ... accuracy: 0.9...
<pypuf.simulation.delay.TwoBitBeliPUF object at 0x...>
>>> model = attack.model

Expand Down
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
130 changes: 128 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,129 @@ 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.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 e473eb0

Please sign in to comment.