GMTROM/Tets_Python/TotOnline.py

218 lines
7.6 KiB
Python
Raw Normal View History

2023-05-09 11:47:47 +02:00
from math import log as log
from numpy import zeros as zeros
from math import fabs as fabs
from math import floor as floor
from math import sqrt as sqrt
from scipy.special import erfc as erfc
from scipy.special import gammaincc as gammaincc
class TotOnline:
2023-05-09 11:47:47 +02:00
@staticmethod
def total_failure_test(binary_data: bytes, pattern_length=10):
length_of_binary_data = len(binary_data) * 8
2023-05-09 11:47:47 +02:00
# Convert bytes to binary string representation
binary_data_str = ''
for byte in binary_data:
binary_data_str += format(byte, '08b')
2023-05-09 11:47:47 +02:00
# Augment the n-bit sequence to create n overlapping m-bit sequences by appending m-1 bits
# from the beginning of the sequence to the end of the sequence.
binary_data_str += binary_data_str[:pattern_length + 1]
2023-05-09 11:47:47 +02:00
# Get max length one patterns for m, m-1, m-2
max_pattern = '1' * (pattern_length + 2)
2023-05-09 11:47:47 +02:00
# Keep track of each pattern's frequency (how often it appears)
vobs_01 = zeros(int(max_pattern[0:pattern_length], 2) + 1)
vobs_02 = zeros(int(max_pattern[0:pattern_length + 1], 2) + 1)
2023-05-09 11:47:47 +02:00
for i in range(length_of_binary_data):
# Work out what pattern is observed
vobs_01[int(binary_data_str[i:i + pattern_length], 2)] += 1
vobs_02[int(binary_data_str[i:i + pattern_length + 1], 2)] += 1
2023-05-09 11:47:47 +02:00
# Calculate the test statistics and p values
vObs = [vobs_01, vobs_02]
sums = zeros(2)
for i in range(2):
for j in range(len(vObs[i])):
if vObs[i][j] > 0:
sums[i] += vObs[i][j] * log(vObs[i][j] / length_of_binary_data)
sums /= length_of_binary_data
ape = sums[0] - sums[1]
xObs = 2.0 * length_of_binary_data * (log(2) - ape)
p_value = gammaincc(pow(2, pattern_length - 1), xObs / 2.0)
return p_value, (p_value >= 0.01)
@staticmethod
def monobit_test(binary_data: bytes):
length_of_bit_string = len(binary_data) * 8
2023-05-09 11:47:47 +02:00
# Variable for S(n)
2023-05-09 11:47:47 +02:00
count = 0
# Iterate each bit in the byte sequence and compute for S(n)
for byte in binary_data:
for bit in range(8):
if (byte >> (7 - bit)) & 1 == 0:
# If bit is 0, then -1 from the S(n)
count -= 1
else:
# If bit is 1, then +1 to the S(n)
count += 1
# Compute the test statistic
2023-05-09 11:47:47 +02:00
sObs = count / sqrt(length_of_bit_string)
# Compute p-Value
2023-05-09 11:47:47 +02:00
p_value = erfc(fabs(sObs) / sqrt(2))
# return a p_value and randomness result
2023-05-09 11:47:47 +02:00
return p_value, (p_value >= 0.01)
@staticmethod
def block_frequency_test(binary_data: bytes, block_size=128):
length_of_bit_string = len(binary_data) * 8
2023-05-09 11:47:47 +02:00
if length_of_bit_string < block_size:
block_size = length_of_bit_string
# Compute the number of blocks based on the input given. Discard the remainder
number_of_blocks = length_of_bit_string // block_size
2023-05-09 11:47:47 +02:00
if number_of_blocks == 1:
# For block size M=1, this test degenerates to test 1, the Frequency (Monobit) test.
return TotOnline.monobit_test(binary_data[0:block_size])
# Initialize variables
2023-05-09 11:47:47 +02:00
proportion_sum = 0.0
# Create a for loop to process each block
for counter in range(number_of_blocks):
# Partition the input sequence and get the data for block
block_start = counter * block_size // 8
block_end = block_start + block_size // 8
2023-05-09 11:47:47 +02:00
block_data = binary_data[block_start:block_end]
# Determine the proportion πi of ones in each M-bit
2023-05-09 11:47:47 +02:00
one_count = 0
for byte in block_data:
for bit in range(8):
if (byte >> (7 - bit)) & 1 == 1:
one_count += 1
# Compute π
pi = one_count / (block_size * 8) * 8
2023-05-09 11:47:47 +02:00
# Compute Σ(πi -½)^2.
proportion_sum += pow(pi - 0.5, 2.0)
# Compute 4M Σ(πi -½)^2.
result = 4.0 * block_size * proportion_sum
# Compute P-Value
p_value = gammaincc(number_of_blocks / 2, result / 2)
return p_value, (p_value >= 0.01)
@staticmethod
def run_test(binary_data: bytes):
2023-05-09 11:47:47 +02:00
vObs = 0
length_of_binary_data = len(binary_data) * 8
2023-05-09 11:47:47 +02:00
# Predefined tau = 2 / sqrt(n)
tau = 2 / sqrt(length_of_binary_data)
# Step 1 - Compute the pre-test proportion π of ones in the input sequence: π = Σjεj / n
one_count = 0
for byte in binary_data:
for bit in range(8):
if (byte >> (7 - bit)) & 1 == 1:
one_count += 1
2023-05-09 11:47:47 +02:00
pi = one_count / length_of_binary_data
# Step 2 - If it can be shown that the absolute value of (π - 0.5) is greater than or equal to tau,
2023-05-09 11:47:47 +02:00
# then the run test need not be performed.
if abs(pi - 0.5) >= tau:
return 0.0000
else:
# Step 3 - Compute vObs
for i in range(1, length_of_binary_data):
if ((binary_data[i // 8] >> (7 - (i % 8))) & 1) != ((binary_data[(i - 1) // 8] >> (7 - ((i - 1) % 8))) & 1):
2023-05-09 11:47:47 +02:00
vObs += 1
vObs += 1
# Step 4 - Compute p_value = erfc((|vObs 2nπ * (1π)|)/(2 * sqrt(2n) * π * (1π)))
p_value = erfc(abs(vObs - (2 * length_of_binary_data * pi * (1 - pi))) / (2 * sqrt(2 * length_of_binary_data) * pi * (1 - pi)))
return p_value, (p_value > 0.01)
@staticmethod
def longest_one_block_test(binary_data: bytes):
length_of_binary_data = len(binary_data) * 8
2023-05-09 11:47:47 +02:00
if length_of_binary_data < 128:
return 0.00000, 'Error: Not enough data to run this test'
elif length_of_binary_data < 6272:
k = 3
m = 8
v_values = [1, 2, 3, 4]
pi_values = [0.2148, 0.3672, 0.2305, 0.1875]
elif length_of_binary_data < 750000:
k = 5
m = 128
v_values = [4, 5, 6, 7, 8, 9]
pi_values = [0.1174, 0.2430, 0.2493, 0.1752, 0.1027, 0.1124]
else:
k = 6
m = 10000
v_values = [10, 11, 12, 13, 14, 15, 16]
pi_values = [0.0882, 0.2092, 0.2483, 0.1933, 0.1208, 0.0675, 0.0727]
number_of_blocks = floor(length_of_binary_data / m)
block_start = 0
block_end = m
xObs = 0
frequencies = zeros(k + 1)
for count in range(number_of_blocks):
block_data = binary_data[block_start // 8:block_end // 8]
2023-05-09 11:47:47 +02:00
max_run_count = 0
run_count = 0
for byte in block_data:
for bit in range(7, -1, -1):
if (byte >> bit) & 1 == 1:
run_count += 1
max_run_count = max(max_run_count, run_count)
else:
max_run_count = max(max_run_count, run_count)
run_count = 0
2023-05-09 11:47:47 +02:00
max(max_run_count, run_count)
if max_run_count < v_values[0]:
frequencies[0] += 1
for j in range(k):
if max_run_count == v_values[j]:
frequencies[j] += 1
if max_run_count > v_values[k - 1]:
frequencies[k] += 1
block_start += m
block_end += m
for count in range(len(frequencies)):
xObs += pow((frequencies[count] - (number_of_blocks * pi_values[count])), 2.0) / (
number_of_blocks * pi_values[count])
p_value = gammaincc(float(k / 2), float(xObs / 2))
return p_value, (p_value > 0.01)