Skip to content

Commit

Permalink
adds simulation, attack, and metrics related to optical PUFs
Browse files Browse the repository at this point in the history
Co-Authored-By: Adomas Baliuka <[email protected]>
  • Loading branch information
nils-wisiol and Adomas Baliuka committed Sep 27, 2021
1 parent ed63388 commit c23ba88
Show file tree
Hide file tree
Showing 15 changed files with 385 additions and 8 deletions.
2 changes: 2 additions & 0 deletions CITATION.cff
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,6 @@ authors:
given-names: Niklas
- family-names: Mursi
given-names: Khalid T.
- family-names: Baliuka
given-names: Adomas
license: GPL 3.0
6 changes: 4 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,8 @@ To refer to pypuf, please use DOI `10.5281/zenodo.3901410`.
pypuf is published [via Zenodo](https://zenodo.org/badge/latestdoi/87066421).
Please cite this work as

> Nils Wisiol, Christoph Gräbnitz, Christopher Mühl, Benjamin Zengin, Tudor Soroceanu, Niklas Pirnay, & Khalid T. Mursi.
> Nils Wisiol, Christoph Gräbnitz, Christopher Mühl, Benjamin Zengin, Tudor Soroceanu, Niklas Pirnay, Khalid T. Mursi,
> & Adomas Baliuka.
> pypuf: Cryptanalysis of Physically Unclonable Functions (Version 2, June 2021). Zenodo.
> https://doi.org/10.5281/zenodo.3901410
Expand All @@ -53,7 +54,8 @@ or use the following BibTeX:
Benjamin Zengin and
Tudor Soroceanu and
Niklas Pirnay and
Khalid T. Mursi},
Khalid T. Mursi and
Adomas Baliuka},
title = {{pypuf: Cryptanalysis of Physically Unclonable
Functions}},
year = 2021,
Expand Down
1 change: 1 addition & 0 deletions docs/appendix/bibliography.rst
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ Bibliography
.. [NSJM19] Nguyen, P. H. et al. The Interpose PUF: Secure PUF Design against State-of-the-art Machine Learning Attacks.
IACR Transactions on Cryptographic Hardware and Embedded Systems 243–290 (2019) doi:10.13154/tches.v2019.i4.243-290.
.. [ODon14] O’Donnell, R. Analysis of Boolean Functions. (Cambridge University Press, 2014).
.. [RHUWDFJ13] Rührmair, U. et al. Optical PUFs Reloaded. https://eprint.iacr.org/2013/215 (2013).
.. [RSSD10] Rührmair, U. et al. Modeling Attacks on Physical Unclonable Functions. in Proceedings of the 17th ACM
Conference on Computer and Communications Security 237–249 (ACM, 2010). doi:10.1145/1866307.1866335.
.. [SD07] Suh, G. E. & Devadas, S. Physical Unclonable Functions for Device Authentication and Secret Key Generation.
Expand Down
116 changes: 116 additions & 0 deletions docs/attacks/linear_regression.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
Linear Regression
=================

Linear Regression fits a linear function on given data. The resulting linear function, also called map, is guaranteed
to be optimal with respect to the total squared error, i.e. the sum of the squared differences of actual value and
predicted value.

Linear Regression has many applications, in pypuf, it can be used to model :doc:`../simulation/optical` and
:doc:`../simulation/arbiter_puf`.


Arbiter PUF Reliability Side-Channel Attack [DV13]_
---------------------------------------------------

For Arbiter PUFs, the reliability for any given challenge :math:`c` has a close relationship with the difference in
delay for the top and bottom line. When modeling the Arbiter PUF response as

.. math::
r = \text{sgn}\left[ D_\text{noise} + \langle w, x \rangle \right],
where :math:`x` is the feature vector corresponding to the challenge :math:`c` and :math:`w \in \mathbb{R}^n` are the
weights describing the Arbiter PUF, and :math:`D_\text{noise}` is chosen from a Gaussian distribution with zero mean
and variance :math:`\sigma_\text{noise}^2` to model the noise, then we can conclude that

.. math::
\text{E}[r(x)] = \text{erf}\left( \frac{\langle w, x \rangle}{\sqrt{2}\sigma_\text{noise}} \right).
Hence, the delay difference :math:`\langle w, x \rangle` can be approximated based on an approximation of
:math:`\text{E[r(x)]}`, which can be easily obtained by an attacker. It gives

.. math::
\langle w, x \rangle = \sqrt{2}\sigma_\text{noise} \cdot \text{erf}^{-1} \text{E}[r(x)].
This approximation works well even when :math:`\text{E}[r(x)]` is approximated based on only on few responses, e.g. 3
(see below).

To demonstrate the attack, we initialize an Arbiter PUF simulation with noisiness chosen such that the reliability
will be about 91% *on average*:

>>> import pypuf.simulation, pypuf.io, pypuf.attack, pypuf.metrics
>>> puf = pypuf.simulation.ArbiterPUF(n=64, noisiness=.25, seed=3)
>>> pypuf.metrics.reliability(puf, seed=3).mean()
0.908...

We then create a CRP set using the *average* value of responses to 500 challenges, based on 5 measurements:

>>> challenges = pypuf.io.random_inputs(n=puf.challenge_length, N=500, seed=2)
>>> responses_mean = puf.r_eval(5, challenges).mean(axis=-1)
>>> crps = pypuf.io.ChallengeResponseSet(challenges, responses_mean)

Based on these approximated values ``responses_mean`` of the linear function :math:`\langle w, x \rangle`, we use
linear regression to find a linear mapping with small error to fit the data. Note that we use the ``transform_atf``
function to compute the feature vector :math:`x` from the challenges :math:`c`, as the mapping is linear in :math:`x`
(but not in :math:`c`).

>>> attack = pypuf.attack.LeastSquaresRegression(crps, feature_map=lambda cs: pypuf.simulation.ArbiterPUF.transform_atf(cs, k=1)[:, 0, :])
>>> model = attack.fit()

The linear map ``model`` will predict the delay difference of a given challenge. To obtain the predicted PUF response,
this prediction needs to be thresholded to either -1 or 1:

>>> model.postprocessing = model.postprocessing_threshold

To measure the resulting model accuracy, we use :meth:`pypuf.metrics.similarity`:

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


Modeling Attack on Integrated Optical PUFs [RHUWDFJ13]_
-------------------------------------------------------

The behavior of an integrated optical PUF token can be understood as a linear map
:math:`T \in \mathbb{C}^{n \times m}` of the given challenge, where the value of :math:`T` are determined by the given
PUF token, and :math:`n` is number of challenge pixels, and :math:`m` the number of response pixels.
The speckle pattern of the PUF is a measurement of the intensity of its electromagnetic field at the output, hence the
intensity at a given response pixel :math:`r_i` for a given challenge :math:`c` can be written as

.. math::
r_i = \left| c \cdot T \right|^2.
pypuf ships a basic simulator for the responses of :doc:`../simulation/optical`, on whose data a modeling attack
can be demonstrated. We first initialize a simulation and collect challenge-response pairs:

>>> puf = pypuf.simulation.IntegratedOpticalPUF(n=64, m=25, seed=1)
>>> crps = pypuf.io.ChallengeResponseSet.from_simulation(puf, N=1000, seed=2)

Then, we fit a linear map on the data contained in ``crps``. Note that the simulation returns *intensity* values rather
than *field* values. We thus need to account for quadratic terms using an appropriate
:meth:`feature map <pypuf.attack.LeastSquaresRegression.feature_map_optical_pufs_reloaded_improved>`.

>>> attack = pypuf.attack.LeastSquaresRegression(crps, feature_map=pypuf.attack.LeastSquaresRegression.feature_map_optical_pufs_reloaded_improved)
>>> model = attack.fit()

The success of the attack can be visually inspected or quantified by the :doc:`/metrics/correlation` of the response
pixels:

>>> crps_test = pypuf.io.ChallengeResponseSet.from_simulation(puf, N=1000, seed=3)
>>> pypuf.metrics.correlation(model, crps_test).mean()
0.69...

Note that the correlation can differ when additionally, post-processing of the responses is performed, e.g. by
thresholding the values such that half the values give -1 and the other half 1:

>>> import numpy as np
>>> threshold = lambda r: np.sign(r - np.quantile(r.flatten(), .5))
>>> pypuf.metrics.correlation(model, crps_test, postprocessing=threshold).mean()
0.41...


API
---

.. autoclass::
pypuf.attack.LeastSquaresRegression
:members: __init__, fit, model, feature_map_optical_pufs_reloaded, feature_map_optical_pufs_reloaded_improved
10 changes: 7 additions & 3 deletions docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ Some functionality is only implemented in the
simulation/arbiter_puf
simulation/delay
simulation/bistable
simulation/optical
simulation/base

.. toctree::
Expand All @@ -36,6 +37,7 @@ Some functionality is only implemented in the
metrics/reliability
metrics/uniqueness
metrics/distance
metrics/correlation
Fourier-Analysis (PUFMeter) <metrics/fourier>

.. toctree::
Expand All @@ -47,6 +49,7 @@ Some functionality is only implemented in the
Logistic Regression <attacks/lr>
Multilayer Perceptron <attacks/mlp>
LMN (PUFMeter) <attacks/lmn>
attacks/linear_regression

.. toctree::
:maxdepth: 2
Expand Down Expand Up @@ -105,8 +108,8 @@ pypuf is published `via Zenodo <https://zenodo.org/badge/latestdoi/87066421>`_.
Please cite this work as

Nils Wisiol, Christoph Gräbnitz, Christopher Mühl, Benjamin Zengin, Tudor Soroceanu,
Niklas Pirnay, & Khalid T. Mursi.
pypuf: Cryptanalysis of Physically Unclonable Functions (Version v2, March 2021). Zenodo.
Niklas Pirnay, Khalid T. Mursi, & Adomas Baliuka.
pypuf: Cryptanalysis of Physically Unclonable Functions (Version v2, August 2021). Zenodo.
https://doi.org/10.5281/zenodo.3901410

or use the following BibTeX::
Expand All @@ -118,7 +121,8 @@ or use the following BibTeX::
Benjamin Zengin and
Tudor Soroceanu and
Niklas Pirnay and
Khalid T. Mursi},
Khalid T. Mursi and
Adomas Baliuka},
title = {{pypuf: Cryptanalysis of Physically Unclonable
Functions}},
year = 2021,
Expand Down
26 changes: 26 additions & 0 deletions docs/metrics/correlation.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
Correlation
-----------

The correlation metric is useful to judge the prediction accuracy when responses are non-binary, e.g. when studying
:doc:`/simulation/optical`.

>>> import pypuf.simulation, pypuf.io, pypuf.attack, pypuf.metrics
>>> puf = pypuf.simulation.IntegratedOpticalPUF(n=64, m=25, seed=1)
>>> crps = pypuf.io.ChallengeResponseSet.from_simulation(puf, N=1000, seed=2)
>>> crps_test = pypuf.io.ChallengeResponseSet.from_simulation(puf, N=1000, seed=3)
>>> feature_map = pypuf.attack.LeastSquaresRegression.feature_map_optical_pufs_reloaded_improved
>>> model = pypuf.attack.LeastSquaresRegression(crps, feature_map=feature_map).fit()
>>> pypuf.metrics.correlation(model, crps_test).mean()
0.69...

Note that the correlation can differ when additionally, post-processing of the responses is performed, e.g. by
thresholding the values such that half the values give -1 and the other half 1:

>>> import numpy as np
>>> threshold = lambda r: np.sign(r - np.quantile(r.flatten(), .5))
>>> pypuf.metrics.correlation(model, crps_test, postprocessing=threshold).mean()
0.41...

.. automodule:: pypuf.metrics
:members: correlation, correlation_data
:noindex:
8 changes: 8 additions & 0 deletions docs/simulation/optical.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
Integrated Optical PUFs
=======================

pypuf ships a very basic simulation of integrated optical PUFs [RHUWDFJ13]_.

.. autoclass::
pypuf.simulation.IntegratedOpticalPUF
:members: __init__, eval
1 change: 1 addition & 0 deletions docs/simulation/overview.rst
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ pypuf currently features simulation of the following strong PUFs:
Lightweight Secure PUF [MKP08]_, Permutation PUF [WBMS19]_, and Interpose PUF [NSJM19]_.
2. :doc:`Feed-Forward Arbiter PUFs <arbiter_puf>` and XORs thereof [GLCDD04]_.
3. :doc:`PUF designs based on bistable rings <bistable>` [CCLSR11]_ [XRHB15]_.
4. :doc:`Integrated Optical PUFs <optical>` [RHUWDFJ13]_.

Technicalities
--------------
Expand Down
2 changes: 2 additions & 0 deletions pypuf/attack/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,5 @@
from .mlp2021 import MLPAttack2021

from .fourier import LMNAttack

from .linear_regression import LeastSquaresRegression
104 changes: 104 additions & 0 deletions pypuf/attack/linear_regression.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
from typing import Callable, Optional

import numpy as np

from .base import OfflineAttack
from ..io import ChallengeResponseSet
from ..simulation.base import Simulation


class LinearMapSimulation(Simulation):

@staticmethod
def postprocessing_id(responses: np.ndarray) -> np.ndarray:
return responses

@staticmethod
def postprocessing_threshold(responses: np.ndarray) -> np.ndarray:
return np.sign(responses)

def __init__(self, linear_map: np.ndarray, challenge_length: int,
feature_map: Optional[Callable[[np.ndarray], np.ndarray]] = None,
postprocessing: Optional[Callable[[np.ndarray], np.ndarray]] = None) -> None:
super().__init__()
self.map = linear_map
self._challenge_length = challenge_length
self.feature_map = feature_map or (lambda x: x)
self.postprocessing = postprocessing or self.postprocessing_id

@property
def challenge_length(self) -> int:
return self._challenge_length

@property
def response_length(self) -> int:
return self.map.shape[1]

def eval(self, challenges: np.ndarray) -> np.ndarray:
return self.postprocessing(self.feature_map(challenges) @ self.map)


class LeastSquaresRegression(OfflineAttack):

@staticmethod
def feature_map_linear(challenges: np.ndarray) -> np.ndarray:
return challenges

@staticmethod
def feature_map_optical_pufs_reloaded(challenges: np.ndarray) -> np.ndarray:
"""
Computes features of an optical PUF token using all ordered pairs of challenge bits [RHUWDFJ13]_.
An optical system may be linear in these features.
.. note::
This representation is redundant since it treats ordered paris of challenge bits are distinct.
Actually, only unordered pairs of bits should be treated as distinct. For applications, use
the function :meth:`feature_map_optical_pufs_reloaded_improved
<pypuf.attack.LeastSquaresRegression.feature_map_optical_pufs_reloaded_improved>`,
which achieves the same with half the number of features.
:param challenges: array of shape :math:`(N, n)` representing challenges to the optical PUF.
:return: array of shape :math:`(N, n^2)`, which, for each challenge, contains the flattened dyadic product of
the challenge with itself.
"""
beta = np.einsum("...i,...j->...ij", challenges, challenges)
return beta.reshape(beta.shape[:-2] + (-1,))

@staticmethod
def feature_map_optical_pufs_reloaded_improved(challenges: np.ndarray) -> np.ndarray:
r"""
Computes features of an optical PUF token using all unordered pairs of challenge bits [RHUWDFJ13]_.
An optical system may be linear in these features.
:param challenges: 2d array of shape :math:`(N, n)` representing `N` challenges of length :math:`n`.
:return: array of shape :math:`(N, \frac{n \cdot (n + 1)}{2})`. The result `return[i]` consists of all products
of unordered pairs taken from `challenges[i]`, which has shape `(N,)`.
>>> import numpy as np
>>> import pypuf.attack
>>> challenges = np.array([[2, 3, 5], [1, 0, 1]]) # non-binary numbers for illustration only.
>>> pypuf.attack.LeastSquaresRegression.feature_map_optical_pufs_reloaded_improved(challenges)
array([[ 4, 6, 10, 9, 15, 25],
[ 1, 0, 1, 0, 0, 1]])
"""
n = challenges.shape[1]
idx = np.triu_indices(n)
return np.einsum("...i,...j->...ij", challenges, challenges)[:, idx[0], idx[1]]

def __init__(self, crps: ChallengeResponseSet,
feature_map: Optional[Callable[[np.ndarray], np.ndarray]] = None) -> None:
super().__init__(crps)
self.crps = crps
self.feature_map = feature_map or (lambda x: x)
self._model = None

def fit(self) -> Simulation:
features = self.feature_map(self.crps.challenges)
# TODO warn if more than one measurement
linear_map = np.linalg.pinv(features) @ self.crps.responses[:, :, 0]
self._model = LinearMapSimulation(linear_map, self.crps.challenge_length, self.feature_map)
return self.model

@property
def model(self) -> Simulation:
return self._model
2 changes: 1 addition & 1 deletion pypuf/metrics/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
from .common import reliability_data, reliability, uniqueness_data, uniqueness, similarity_data, accuracy, similarity, \
bias, bias_data
bias, bias_data, correlation, correlation_data
from .fourier import total_influence, noise_sensitivity, influence
Loading

0 comments on commit c23ba88

Please sign in to comment.