Nilorea Library
C utilities for networking, threading, graphics
Loading...
Searching...
No Matches
n_entropy.c
Go to the documentation of this file.
1/*
2 * Nilorea Library
3 * Copyright (C) 2005-2026 Castagnier Mickael
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
14 * implied. See the License for the specific language governing
15 * permissions and limitations under the License.
16 *
17 * SPDX-License-Identifier: Apache-2.0
18 */
19
24
25#include "nilorea/n_entropy.h"
26
27#include <math.h>
28
29double n_entropy_shannon(const unsigned char* data, size_t len) {
30 size_t counts[256];
31 size_t i;
32 double h = 0.0;
33 if (!data || len == 0)
34 return 0.0;
35 for (i = 0; i < 256; i++)
36 counts[i] = 0;
37 for (i = 0; i < len; i++)
38 counts[data[i]]++;
39 for (i = 0; i < 256; i++) {
40 if (counts[i]) {
41 double p = (double)counts[i] / (double)len;
42 h -= p * log2(p);
43 }
44 }
45 return h;
46}
47
48double n_entropy_monobit(const unsigned char* data, size_t len) {
49 size_t i;
50 size_t ones = 0;
51 if (!data || len == 0)
52 return 0.0;
53 for (i = 0; i < len; i++) {
54 unsigned int b = data[i];
55 /* popcount of one byte */
56 b = b - ((b >> 1) & 0x55u);
57 b = (b & 0x33u) + ((b >> 2) & 0x33u);
58 b = (b + (b >> 4)) & 0x0Fu;
59 ones += b;
60 }
61 return (double)ones / ((double)len * 8.0);
62}
63
64double n_entropy_chi_square(const unsigned char* data, size_t len) {
65 size_t counts[256];
66 size_t i;
67 double expected, chi = 0.0;
68 if (!data || len == 0)
69 return 0.0;
70 for (i = 0; i < 256; i++)
71 counts[i] = 0;
72 for (i = 0; i < len; i++)
73 counts[data[i]]++;
74 expected = (double)len / 256.0;
75 for (i = 0; i < 256; i++) {
76 double diff = (double)counts[i] - expected;
77 chi += (diff * diff) / expected;
78 }
79 return chi;
80}
81
82double n_entropy_chi_square_p(double chi, size_t df) {
83 double d, t, z;
84 if (df == 0 || chi <= 0.0)
85 return 1.0;
86 d = (double)df;
87 /* Wilson-Hilferty: (chi2/df)^(1/3) is close to normal with mean 1-2/(9df)
88 and variance 2/(9df), so the upper tail becomes a normal tail */
89 t = cbrt(chi / d);
90 z = (t - (1.0 - 2.0 / (9.0 * d))) / sqrt(2.0 / (9.0 * d));
91 return 0.5 * erfc(z / sqrt(2.0));
92}
93
94size_t n_entropy_positions(const unsigned char* const* samples, size_t count, size_t sample_len, N_ENTROPY_POS* out, size_t max_out) {
95 size_t pos, n;
96 if (!samples || !out || count == 0 || sample_len == 0 || max_out == 0)
97 return 0;
98 n = (sample_len < max_out) ? sample_len : max_out;
99 for (pos = 0; pos < n; pos++) {
100 size_t counts[256];
101 size_t i, distinct = 0;
102 double h = 0.0, chi = 0.0;
103 for (i = 0; i < 256; i++)
104 counts[i] = 0;
105 for (i = 0; i < count; i++)
106 if (samples[i])
107 counts[samples[i][pos]]++;
108 for (i = 0; i < 256; i++)
109 if (counts[i]) {
110 double p = (double)counts[i] / (double)count;
111 distinct++;
112 h -= p * log2(p);
113 }
114 /* the chi-square is taken over the alphabet ACTUALLY seen, not over all 256
115 values: a hex token uses 16 of them by design, and scoring it against 256
116 would report every well-formed token as catastrophically biased */
117 if (distinct > 0) {
118 double expected = (double)count / (double)distinct;
119 for (i = 0; i < 256; i++)
120 if (counts[i]) {
121 double diff = (double)counts[i] - expected;
122 chi += (diff * diff) / expected;
123 }
124 }
125 out[pos].distinct = distinct;
126 out[pos].shannon = h;
127 out[pos].chi_square = chi;
128 out[pos].p_value = n_entropy_chi_square_p(chi, distinct > 0 ? distinct - 1 : 0);
129 }
130 return n;
131}
132
133size_t n_entropy_bits(const unsigned char* const* samples, size_t count, size_t sample_len, N_ENTROPY_BIT* out, size_t max_out) {
134 size_t bit, nbits;
135 if (!samples || !out || count == 0 || sample_len == 0 || max_out == 0)
136 return 0;
137 nbits = sample_len * 8;
138 if (nbits > max_out)
139 nbits = max_out;
140 for (bit = 0; bit < nbits; bit++) {
141 size_t byte = bit / 8;
142 unsigned mask = (unsigned)(0x80u >> (bit % 8));
143 size_t i, ones = 0;
144 double frac, z;
145 for (i = 0; i < count; i++)
146 if (samples[i] && (samples[i][byte] & mask))
147 ones++;
148 frac = (double)ones / (double)count;
149 /* z of the imbalance against a fair coin: (ones - n/2) / (sqrt(n)/2) */
150 z = ((double)ones - (double)count / 2.0) / (sqrt((double)count) / 2.0);
151 out[bit].ones = ones;
152 out[bit].fraction = frac;
153 out[bit].z = z;
154 out[bit].p_value = erfc(fabs(z) / sqrt(2.0));
155 }
156 return nbits;
157}
158
159double n_entropy_runs_p(const unsigned char* data, size_t len) {
160 size_t nbits, i, runs = 1;
161 double pi, z;
162 int prev;
163 if (!data || len == 0)
164 return 0.0;
165 nbits = len * 8;
166 pi = n_entropy_monobit(data, len);
167 /* the test is only meaningful when the stream is roughly balanced; outside that
168 band monobit has already failed and this would divide by a vanishing term */
169 if (fabs(pi - 0.5) >= 2.0 / sqrt((double)nbits))
170 return 0.0;
171 prev = (data[0] & 0x80u) ? 1 : 0;
172 for (i = 1; i < nbits; i++) {
173 int b = (data[i / 8] & (unsigned)(0x80u >> (i % 8))) ? 1 : 0;
174 if (b != prev)
175 runs++;
176 prev = b;
177 }
178 z = fabs((double)runs - 2.0 * (double)nbits * pi * (1.0 - pi)) /
179 (2.0 * sqrt(2.0 * (double)nbits) * pi * (1.0 - pi));
180 return erfc(z / sqrt(2.0));
181}
182
183size_t n_entropy_longest_run(const unsigned char* data, size_t len) {
184 size_t nbits, i, best = 0, cur = 1;
185 int prev;
186 if (!data || len == 0)
187 return 0;
188 nbits = len * 8;
189 prev = (data[0] & 0x80u) ? 1 : 0;
190 best = 1;
191 for (i = 1; i < nbits; i++) {
192 int b = (data[i / 8] & (unsigned)(0x80u >> (i % 8))) ? 1 : 0;
193 if (b == prev) {
194 cur++;
195 if (cur > best)
196 best = cur;
197 } else {
198 cur = 1;
199 }
200 prev = b;
201 }
202 return best;
203}
double p_value
upper-tail p-value of chi_square
Definition n_entropy.h:70
double chi_square
statistic against a uniform spread over the values seen
Definition n_entropy.h:69
size_t ones
samples whose bit is set
Definition n_entropy.h:75
size_t distinct
distinct byte values seen at this position
Definition n_entropy.h:67
double p_value
two-sided p-value of z
Definition n_entropy.h:78
double fraction
ones / sample count (0.5 is ideal)
Definition n_entropy.h:76
double shannon
bits of entropy at this position (0 .
Definition n_entropy.h:68
double z
z-score of the imbalance against a fair coin
Definition n_entropy.h:77
size_t n_entropy_positions(const unsigned char *const *samples, size_t count, size_t sample_len, N_ENTROPY_POS *out, size_t max_out)
Per-POSITION analysis of a set of equal-length samples.
Definition n_entropy.c:94
size_t n_entropy_bits(const unsigned char *const *samples, size_t count, size_t sample_len, N_ENTROPY_BIT *out, size_t max_out)
Per-BIT analysis of a set of equal-length samples.
Definition n_entropy.c:133
size_t n_entropy_longest_run(const unsigned char *data, size_t len)
Length of the longest run of identical bits in the sample.
Definition n_entropy.c:183
double n_entropy_shannon(const unsigned char *data, size_t len)
Shannon entropy of the byte sample, in bits per byte (0.0 .
Definition n_entropy.c:29
double n_entropy_monobit(const unsigned char *data, size_t len)
Fraction of set bits in the sample (0.0 .
Definition n_entropy.c:48
double n_entropy_chi_square(const unsigned char *data, size_t len)
Chi-square statistic of the byte histogram against a uniform distribution over 256 values.
Definition n_entropy.c:64
double n_entropy_chi_square_p(double chi, size_t df)
Upper-tail p-value of a chi-square statistic (Wilson-Hilferty).
Definition n_entropy.c:82
double n_entropy_runs_p(const unsigned char *data, size_t len)
Runs test over the sample's bit stream (NIST SP 800-22 style).
Definition n_entropy.c:159
per-bit statistics across a set of equal-length samples
Definition n_entropy.h:74
per-position statistics across a set of equal-length samples
Definition n_entropy.h:66
Randomness/entropy metrics for byte samples (token-randomness analysis).