Reliability

Reliability is a measure for the noise in challenge evaluation of a single PUF instance. Most PUF implementations have challenge evaluation mechanisms influenced by noise, hence the question of reproducible responses arises. In the literature, the concept of reliability is also referred to as intra-distance, stability and reproducibility. pypuf defines the reliability on challenge \(x\) of a PUF with responses \(\mathtt{eval}(x)\) to be

\[\Pr_\mathtt{eval} \left[ \mathtt{eval}(x) = \mathtt{eval}(x) \right],\]

where the probability is taken over the noise in the evaluation process \(\mathtt{eval}\). The general reliability of a PUF is the average over all challenges,

\[E_x \left[ \Pr_\mathtt{eval} \left[ \mathtt{eval}(x) = \mathtt{eval}(x) \right] \right].\]

The definition is applied separately for each output bit.

pypuf ships approximations of PUF reliability based on both an instance of pypuf.simulation.Simulation and on response data given. In some PUFs, the reliability depends on the challenge given, i.e. in one instance, some challenges have hard-to-reproduce responses, while others have very stable response behavior. pypuf hence reports reliability information seperatly for each challenge. To obtain a general notion of reliability, results can be averaged along the first axis.

pypuf.metrics.reliability(instance: Simulation, seed: int, N: int = 10000, r: int = 17) ndarray

For a given simulation, estimates simulated reliability.

Randomly generates \(N\) challenges using the \(\mathtt{seed}\), then queries each challenge \(r\) times, obtaining a response array of shape \((N, m, r)\), where \(m\) is the response length of the given simulation.

Then applies reliability_data() to determine the reliability of each respones bit on each challenge.

Returns a float-array of shape \((N, m)\), giving the reliability of each response bit on each challenge. To obtain the general reliability for each response bit, average along the first axis. To obtain the total reliability of this instance, average across all axes. Note that bad reliability on single response bits may be problematic.

>>> from pypuf.simulation import ArbiterPUF, XORArbiterPUF
>>> from pypuf.metrics import reliability
>>> from numpy import average
>>> puf = XORArbiterPUF(n=128, k=2, seed=1, noisiness=.2)
>>> average(reliability(puf, seed=2), axis=0)
array([0.84967612])

An example of an extremely noisy PUF:

>>> average(reliability(XORArbiterPUF(n=32, k=12, seed=1, noisiness=1), seed=2), axis=0)
array([0.52975917])

An example of a very reliable PUF:

>>> average(reliability(ArbiterPUF(n=32, seed=1, noisiness=.01), seed=2), axis=0)
array([0.99582561])

An example of a perfectly reliable PUF:

>>> average(reliability(ArbiterPUF(n=64, seed=1, noisiness=0), seed=2), axis=0)
array([1.])
pypuf.metrics.reliability_data(responses: ndarray) ndarray

Computes the reliability of response data of a PUF instance.

Given responses of a PUF instance in an array of shape \((N, m, r)\), where \(N\) is the number of challenges queries, \(m\) is the response length of the PUF, \(r\) is the number of repeated queries for each challenge, this function computes the empirical reliability of the PUF responses, i.e. an approximation of

\[\Pr_\text{eval} \left[ \text{eval}(x) = \text{eval}(x) \right]\]

where \(\text{eval}(x)\) is the response of the (noisy) PUF when evaluated on input \(x\), and the probability is taken over the noise in the evaluation process.

The approximation is derived from an approximation of \(E_\text{eval} \left[ \text{eval}(x) \right]\), which is given by the mean of responses,

\[\Pr_\text{eval} \left[ \text{eval}(x) = \text{eval}(x) \right] = \frac{1}{2} E_\text{eval} \left[ \text{eval}(x) \right]^2 + \frac{1}{2}.\]

The formula can be obtained by observing \(\Pr_\text{eval} \left[ \text{eval}(x) = \text{eval}(x) \right] = \Pr\left[\text{eval}(x)=1\right]^2 + \Pr\left[\text{eval}(x)=-1\right]^2\) and \(\Pr_\text{eval}\left[\text{eval}(x)=1\right] = \frac{1}{2}E_\text{eval}\left[\text{eval}(x)\right] + \frac{1}{2}\).

An array of shape \((N,m)\) is returned, estimating above probability for each challenge and each response bit. To obtain a the reliability for each response bit, average along the first axis, to obtain the general reliability, average over all axes.

>>> from pypuf.metrics import reliability_data
>>> from numpy import average, array
>>> responses = array([[[1, 1, -1]], [[1, 1, 1]], [[1, 1, 1]], [[1, 1, 1]]])
>>> average(reliability_data(responses), axis=0)
array([0.88888889])