Overview

Generating Uniform Random Challenges

pypuf.io.random_inputs(n: int, N: int, seed: int) ndarray

Generates \(N\) uniformly random challenges of length n and returns them as a numpy.ndarray of shape \((N, n)\). The randomness is based on the provided seed.

Note

pypuf uses \(\{-1,1\}\) to represent bit values for both challenges and responses. To convert from \(\{-1,1\}\) notation to the more traditional \(\{0,1\}\) encoding, use x = (1 - x) // 2. For the reverse conversion, use x = 1 - 2*x

>>> import numpy as np
>>> import pypuf.io
>>> challenges = pypuf.io.random_inputs(n=64, N=10, seed=1)
>>> np.unique(challenges)
array([-1,  1], dtype=int8)
>>> challenges01 = (1 - challenges) // 2
>>> np.unique(challenges01)
array([0, 1], dtype=int8)
>>> challenges11 = 1 - 2 * challenges01
>>> (challenges11 == challenges).all()
True

Storing Challenge-Response Data

pypuf stores challenge-response data in objects of the class pypuf.io.ChallengeResponseSet. These objects contain two numpy arrays with the challenges and responses, respectively and provide a number of auxiliary functions for convenient management.

To create a instance of pypuf.io.ChallengeResponseSet for given challenges and responses, use

>>> import numpy as np
>>> challenges = np.array([[-1, -1, -1, -1], [-1, -1, -1, 1]])
>>> responses = np.array([1, 1])
>>> import pypuf.io
>>> crp = pypuf.io.ChallengeResponseSet(challenges, responses)

To create an instance of pypuf.io.ChallengeResponseSet from a pypuf.simulation.Simulation, use

>>> import pypuf.simulation
>>> puf = pypuf.simulation.ArbiterPUF(n=64, seed=1)
>>> import pypuf.io
>>> crp = pypuf.io.ChallengeResponseSet.from_simulation(puf, N=1000, seed=2)

pypuf CRP data can be stored on disk and loaded back into pypuf:

>>> crp = pypuf.io.ChallengeResponseSet.from_simulation(puf, N=1000, seed=2)
>>> crp.save('crps.npz')
>>> crp_loaded = pypuf.io.ChallengeResponseSet.load('crps.npz')
>>> crp == crp_loaded
True

pypuf.io.ChallengeResponseSet can also be sliced to obtain a single challenge-response pair or to get a subset of CRPs:

>>> crp = pypuf.io.ChallengeResponseSet.from_simulation(puf, N=1000, seed=2)
>>> crp[0]  
(array([-1,...), array([[1.]]))
>>> crp[:10]
<10 CRPs with challenge length 64 and response length 1, each response measured 1 time(s)>
class pypuf.io.ChallengeResponseSet(challenges: ndarray, responses: ndarray)
__init__(challenges: ndarray, responses: ndarray) None

Create a challenge-response object containing the given challenges and responses of a PUF token.

Parameters:
  • challenges (numpy.ndarray) – Challenges to the PUF token organized as array of shape \((N, n)\), where \(N\) is the number of challenges and \(n\) is the challenge length.

  • responses (numpy.ndarray) – Responses of the PUF token organized as array of shape \((N, m, r)\), where \(N\) is the number of challenges, \(m\) is the response length, and \(r\) is the number of measurements per challenge. The last axis is optional.

property challenge_length: int

The length \(n\) of the recorded challenges.

classmethod load(f: str) object

Loads CRPs from the given file f.

property response_length: int

The length \(m\) of the recorded responses.

save(f: str) None

Saves the CRPs to the given file f.