Malware and cryptography 37 - Nonlinearity. Walsh Transform. Simple C example.
﷽
Hello, cybersecurity enthusiasts and white hackers!
In the previous post I wrote about such a property of S blocks as nonlinearity. But how can this nonlinearity be analyzed and calculated? One of such methods is the Fourier transformation, or to be more precise, its subtype: Walsh Transformation. This post is the result of my own research on implementing Walsh Transformation.
the Walsh transform
The Walsh transform is a mathematical transformation that measures how “correlated” a binary sequence is with all possible linear functions. It is widely used in cryptography to analyze the nonlinearity of cryptographic functions, such as S-boxes, which are critical for resisting linear cryptanalysis.
The Walsh transform maps a binary sequence t into another sequence W, where each value in W indicates the “bias” of t towards a specific linear function. The formula for the Walsh transform is:
\[ W(f)(ω) = \sum_x{(−1)}^{f(x) \oplus (inner product(ω,x))} \]
- \( f(x) \) - is the binary function or sequence being transformed.
- inner product(ω,x) - is the dot product of \(ω\) and \(x\) in binary (bitwise AND followed by XOR).
The Walsh transform evaluates how much a binary sequence deviates from being linear. High nonlinearity is crucial for cryptographic primitives because it ensures resistance to linear cryptanalysis, a method that exploits linear relationships between input and output bits.
measuring nonlinearity of S-boxes
To measure the nonlinearity of an S-box
using the Walsh transform:
-
Transform the
S-box
into binary sequences: for each possible output bit, create binary sequences based on whether eachS-box
output bit is0
or1
. -
Apply Walsh transform: for each binary sequence, compute the Walsh transform.
-
Evaluate Nonlinearity: The nonlinearity is the minimum Hamming distance between the sequence and all linear functions.
This can be calculated using the Walsh transform as:
\[Nonlinearity = \frac{N}{2} − \frac{max(abs(W(f)))}{2}\]
Where, \(N\) is the size of the S-box
(e.g., N=256
for an 8
-bit S-box
). \(max(∣W(f)∣)\) is the maximum absolute value of the Walsh transform output.
In other words, nonlinearity measures how far a binary sequence is from the closest linear function.
practical example
Let’s create a simple program to calculate nonlinearity of custom S-boxes.
First of all we need function to compute binary inner product:
int binaryInnerProduct(int a, int b) {
int ip = 0;
int ab = a & b;
while (ab > 0) {
ip ^= (ab & 1);
ab >>= 1;
}
return ip;
}
This is used to evaluate linear combinations.
Then, we need Walsh transormation logic:
// function to compute the Walsh transform of a binary sequence
void walshTransform(int* t, int len, int* wt) {
for (int w = 0; w < len; w++) {
wt[w] = 0;
for (int x = 0; x < len; x++) {
wt[w] += pow(-1, t[x] ^ binaryInnerProduct(w, x));
}
}
}
This function outputs a list of transformed values, representing the correlation with all possible linear functions.
Next one calculates the nonlinearity of the binary sequence:
// function to calculate nonlinearity of a binary sequence
double nonLinearity(int* t, int len) {
int* wt = (int*)malloc(len * sizeof(int));
if (!wt) {
fprintf(stderr, "memory allocation failed.\n");
exit(1);
}
walshTransform(t, len, wt);
int max_wt = 0;
for (int i = 0; i < len; i++) {
if (abs(wt[i]) > max_wt) {
max_wt = abs(wt[i]);
}
}
free(wt);
return len / 2.0 - 0.5 * max_wt;
}
At the final, just create function to calculate s-box nonlinearity:
// function to compute S-box nonlinearity
void sboxNonlinearity(int* sbox, int len) {
double nlv_min = len / 2; // Minimum nonlinearity
double nlv_max = 0; // Maximum nonlinearity
for (int c = 1; c < len; c++) { // Skip zero combination
int* t = (int*)malloc(len * sizeof(int));
if (!t) {
fprintf(stderr, "memory allocation failed.\n");
exit(1);
}
for (int i = 0; i < len; i++) {
t[i] = binaryInnerProduct(c, sbox[i]);
}
double nl = nonLinearity(t, len);
if (nl < nlv_min) {
nlv_min = nl;
}
if (nl > nlv_max) {
nlv_max = nl;
}
free(t);
}
printf("S-box nonlinearity (min, max): (%f, %f)\n", nlv_min, nlv_max);
}
The full source code is looks like this (hack.c
):
/*
* hack.c
* S-box nonlinearity
* via Walsh Transformation
* author: @cocomelonc
* https://cocomelonc.github.io/malware/2024/12/23/malware-cryptography-37.html
*/
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
// function to compute binary inner product
int binaryInnerProduct(int a, int b) {
int ip = 0;
int ab = a & b;
while (ab > 0) {
ip ^= (ab & 1);
ab >>= 1;
}
return ip;
}
// function to compute log2(n), ensuring n is a power of 2
int log2n(int l) {
int n = 0;
int x = l;
while (x > 0) {
x >>= 1;
n++;
}
n--;
if ((1 << n) != l) {
fprintf(stderr, "error: length is not a power of 2.\n");
exit(1);
}
return n;
}
// function to compute the Walsh transform of a binary sequence
void walshTransform(int* t, int len, int* wt) {
for (int w = 0; w < len; w++) {
wt[w] = 0;
for (int x = 0; x < len; x++) {
wt[w] += pow(-1, t[x] ^ binaryInnerProduct(w, x));
}
}
}
// function to calculate nonlinearity of a binary sequence
double nonLinearity(int* t, int len) {
int* wt = (int*)malloc(len * sizeof(int));
if (!wt) {
fprintf(stderr, "memory allocation failed.\n");
exit(1);
}
walshTransform(t, len, wt);
int max_wt = 0;
for (int i = 0; i < len; i++) {
if (abs(wt[i]) > max_wt) {
max_wt = abs(wt[i]);
}
}
free(wt);
return len / 2.0 - 0.5 * max_wt;
}
// function to compute S-box nonlinearity
void sboxNonlinearity(int* sbox, int len) {
double nlv_min = len / 2; // Minimum nonlinearity
double nlv_max = 0; // Maximum nonlinearity
for (int c = 1; c < len; c++) { // Skip zero combination
int* t = (int*)malloc(len * sizeof(int));
if (!t) {
fprintf(stderr, "memory allocation failed.\n");
exit(1);
}
for (int i = 0; i < len; i++) {
t[i] = binaryInnerProduct(c, sbox[i]);
}
double nl = nonLinearity(t, len);
if (nl < nlv_min) {
nlv_min = nl;
}
if (nl > nlv_max) {
nlv_max = nl;
}
free(t);
}
printf("S-box nonlinearity (min, max): (%f, %f)\n", nlv_min, nlv_max);
}
int main() {
// example custom S-box
// int custom_sbox[] = {0x3, 0x6, 0x5, 0xC, 0xA, 0x0, 0xE, 0x9, 0xD, 0x8, 0xF, 0x4, 0x7, 0x1, 0xB, 0x2};
// int custom_sbox[] = {0x4a, 0x49, 0xa4, 0x2c, 0xc2, 0x6e, 0xb5, 0x4f, 0x00, 0xbb, 0x8f, 0x75, 0x19, 0x56, 0x86, 0x77, 0x8e, 0xaa, 0x51, 0x01, 0xff, 0x6d, 0xcf, 0x14, 0x60, 0xc0, 0x7c, 0x22, 0xf4, 0xe0, 0xf5, 0x0a, 0x44, 0x1c, 0x6a, 0x32, 0xe4, 0xad, 0xb4, 0xa8, 0xa3, 0xb2, 0x7f, 0xa0, 0x78, 0x80, 0xa2, 0x50, 0x08, 0x95, 0x8d, 0xaf, 0xd1, 0x10, 0xf8, 0xea, 0xd7, 0x0e, 0x5d, 0xd5, 0x43, 0x68, 0x7d, 0x29, 0xfd, 0x69, 0xd8, 0x59, 0x55, 0x97, 0x94, 0x73, 0xd9, 0xf9, 0xfb, 0x82, 0x31, 0xb9, 0x2f, 0x89, 0x76, 0xe8, 0x99, 0x88, 0x21, 0x7e, 0x2d, 0x92, 0xee, 0xa1, 0xae, 0xd0, 0x36, 0x42, 0x1a, 0x20, 0x03, 0x34, 0x2b, 0xcd, 0x66, 0x71, 0xcc, 0x3d, 0xd4, 0x2e, 0x40, 0xe9, 0x23, 0x70, 0x65, 0xcb, 0xb7, 0x33, 0xce, 0x9d, 0x18, 0xeb, 0x61, 0xa6, 0xfa, 0x3c, 0xbe, 0x53, 0xda, 0x9f, 0xd2, 0x90, 0xfc, 0xf1, 0xd3, 0xf0, 0x24, 0x12, 0xac, 0xbd, 0x5e, 0x4c, 0xc4, 0x38, 0xbf, 0x4e, 0xd6, 0xef, 0x3b, 0x8c, 0xc1, 0x2a, 0xb0, 0x4b, 0x06, 0x5f, 0x1e, 0x0c, 0x46, 0x13, 0xdf, 0xdd, 0x85, 0x57, 0xc3, 0xde, 0xe1, 0x8a, 0xbc, 0xc9, 0x9e, 0xca, 0x83, 0x72, 0x6f, 0xa5, 0x79, 0x54, 0x9b, 0x96, 0x93, 0x30, 0xe3, 0x1b, 0x39, 0xe5, 0x1f, 0x47, 0x3a, 0x64, 0xab, 0x9c, 0x63, 0xf2, 0x16, 0xe2, 0xfe, 0x87, 0xf7, 0x04, 0xdc, 0x5c, 0x7b, 0x15, 0x5a, 0xf6, 0x58, 0x6c, 0xc6, 0xb6, 0x5b, 0x1d, 0xb3, 0xc8, 0x25, 0x7a, 0xf3, 0x02, 0x3f, 0x0b, 0x37, 0x41, 0x3e, 0x4d, 0xe7, 0xc5, 0xc7, 0x67, 0x9a, 0x84, 0xb8, 0x0d, 0x6b, 0x91, 0x11, 0x98, 0x8b, 0x35, 0xb1, 0x09, 0xdb, 0x28, 0x81, 0x17, 0x74, 0xed, 0x62, 0xa7, 0xa9, 0x07, 0x0f, 0xba, 0x27, 0x52, 0xe6, 0x05, 0x48, 0x45, 0x26, 0xec};
// AES encryption S-box
int custom_sbox[] = {0x63,0x7c,0x77,0x7b,0xf2,0x6b,0x6f,0xc5,
0x30,0x01,0x67,0x2b,0xfe,0xd7,0xab,0x76,
0xca,0x82,0xc9,0x7d,0xfa,0x59,0x47,0xf0,
0xad,0xd4,0xa2,0xaf,0x9c,0xa4,0x72,0xc0,
0xb7,0xfd,0x93,0x26,0x36,0x3f,0xf7,0xcc,
0x34,0xa5,0xe5,0xf1,0x71,0xd8,0x31,0x15,
0x04,0xc7,0x23,0xc3,0x18,0x96,0x05,0x9a,
0x07,0x12,0x80,0xe2,0xeb,0x27,0xb2,0x75,
0x09,0x83,0x2c,0x1a,0x1b,0x6e,0x5a,0xa0,
0x52,0x3b,0xd6,0xb3,0x29,0xe3,0x2f,0x84,
0x53,0xd1,0x00,0xed,0x20,0xfc,0xb1,0x5b,
0x6a,0xcb,0xbe,0x39,0x4a,0x4c,0x58,0xcf,
0xd0,0xef,0xaa,0xfb,0x43,0x4d,0x33,0x85,
0x45,0xf9,0x02,0x7f,0x50,0x3c,0x9f,0xa8,
0x51,0xa3,0x40,0x8f,0x92,0x9d,0x38,0xf5,
0xbc,0xb6,0xda,0x21,0x10,0xff,0xf3,0xd2,
0xcd,0x0c,0x13,0xec,0x5f,0x97,0x44,0x17,
0xc4,0xa7,0x7e,0x3d,0x64,0x5d,0x19,0x73,
0x60,0x81,0x4f,0xdc,0x22,0x2a,0x90,0x88,
0x46,0xee,0xb8,0x14,0xde,0x5e,0x0b,0xdb,
0xe0,0x32,0x3a,0x0a,0x49,0x06,0x24,0x5c,
0xc2,0xd3,0xac,0x62,0x91,0x95,0xe4,0x79,
0xe7,0xc8,0x37,0x6d,0x8d,0xd5,0x4e,0xa9,
0x6c,0x56,0xf4,0xea,0x65,0x7a,0xae,0x08,
0xba,0x78,0x25,0x2e,0x1c,0xa6,0xb4,0xc6,
0xe8,0xdd,0x74,0x1f,0x4b,0xbd,0x8b,0x8a,
0x70,0x3e,0xb5,0x66,0x48,0x03,0xf6,0x0e,
0x61,0x35,0x57,0xb9,0x86,0xc1,0x1d,0x9e,
0xe1,0xf8,0x98,0x11,0x69,0xd9,0x8e,0x94,
0x9b,0x1e,0x87,0xe9,0xce,0x55,0x28,0xdf,
0x8c,0xa1,0x89,0x0d,0xbf,0xe6,0x42,0x68,
0x41,0x99,0x2d,0x0f,0xb0,0x54,0xbb,0x16};
int len = sizeof(custom_sbox) / sizeof(custom_sbox[0]);
printf("custom S-box:\n");
for (int i = 0; i < len; i++) {
printf("0x%x ", custom_sbox[i]);
}
printf("\n\n");
sboxNonlinearity(custom_sbox, len);
return 0;
}
As you can see, the program prints the results for few example sequences. You can replace the sequences with your own to analyze other binary sequences.
demo
Let’s go to see everything in action. Compile it:
gcc -o hack hack.c -lm
Run it (AES S-box example):
./hack
The AES S-box is a substitution-permutation matrix used to introduce nonlinearity. It is based on a finite field inversion followed by an affine transformation.
For S-box generated by Fisher-Yates randomization from previous post:
int custom_sbox[] = {0x4a, 0x49, 0xa4, 0x2c, 0xc2, 0x6e, 0xb5, 0x4f, 0x00, 0xbb, 0x8f, 0x75, 0x19, 0x56, 0x86, 0x77, 0x8e, 0xaa, 0x51, 0x01, 0xff, 0x6d, 0xcf, 0x14, 0x60, 0xc0, 0x7c, 0x22, 0xf4, 0xe0, 0xf5, 0x0a, 0x44, 0x1c, 0x6a, 0x32, 0xe4, 0xad, 0xb4, 0xa8, 0xa3, 0xb2, 0x7f, 0xa0, 0x78, 0x80, 0xa2, 0x50, 0x08, 0x95, 0x8d, 0xaf, 0xd1, 0x10, 0xf8, 0xea, 0xd7, 0x0e, 0x5d, 0xd5, 0x43, 0x68, 0x7d, 0x29, 0xfd, 0x69, 0xd8, 0x59, 0x55, 0x97, 0x94, 0x73, 0xd9, 0xf9, 0xfb, 0x82, 0x31, 0xb9, 0x2f, 0x89, 0x76, 0xe8, 0x99, 0x88, 0x21, 0x7e, 0x2d, 0x92, 0xee, 0xa1, 0xae, 0xd0, 0x36, 0x42, 0x1a, 0x20, 0x03, 0x34, 0x2b, 0xcd, 0x66, 0x71, 0xcc, 0x3d, 0xd4, 0x2e, 0x40, 0xe9, 0x23, 0x70, 0x65, 0xcb, 0xb7, 0x33, 0xce, 0x9d, 0x18, 0xeb, 0x61, 0xa6, 0xfa, 0x3c, 0xbe, 0x53, 0xda, 0x9f, 0xd2, 0x90, 0xfc, 0xf1, 0xd3, 0xf0, 0x24, 0x12, 0xac, 0xbd, 0x5e, 0x4c, 0xc4, 0x38, 0xbf, 0x4e, 0xd6, 0xef, 0x3b, 0x8c, 0xc1, 0x2a, 0xb0, 0x4b, 0x06, 0x5f, 0x1e, 0x0c, 0x46, 0x13, 0xdf, 0xdd, 0x85, 0x57, 0xc3, 0xde, 0xe1, 0x8a, 0xbc, 0xc9, 0x9e, 0xca, 0x83, 0x72, 0x6f, 0xa5, 0x79, 0x54, 0x9b, 0x96, 0x93, 0x30, 0xe3, 0x1b, 0x39, 0xe5, 0x1f, 0x47, 0x3a, 0x64, 0xab, 0x9c, 0x63, 0xf2, 0x16, 0xe2, 0xfe, 0x87, 0xf7, 0x04, 0xdc, 0x5c, 0x7b, 0x15, 0x5a, 0xf6, 0x58, 0x6c, 0xc6, 0xb6, 0x5b, 0x1d, 0xb3, 0xc8, 0x25, 0x7a, 0xf3, 0x02, 0x3f, 0x0b, 0x37, 0x41, 0x3e, 0x4d, 0xe7, 0xc5, 0xc7, 0x67, 0x9a, 0x84, 0xb8, 0x0d, 0x6b, 0x91, 0x11, 0x98, 0x8b, 0x35, 0xb1, 0x09, 0xdb, 0x28, 0x81, 0x17, 0x74, 0xed, 0x62, 0xa7, 0xa9, 0x07, 0x0f, 0xba, 0x27, 0x52, 0xe6, 0x05, 0x48, 0x45, 0x26, 0xec};
You can define your custom S-box
as an array of integers in the main function, and the sboxNonlinearity
function will calculate and print its minimum and maximum nonlinearity.
Python implementation:
def walshTransform(t):
n = len(t) # Number of inputs
wt = [0] * n # Initialize Walsh Transform output
for w in range(n):
for x in range(n):
wt[w] += (-1) ** (t[x] ^ binaryInnerProduct(w, x))
return wt
def binaryInnerProduct(a, b):
"""binary inner product of a and b (XOR of ANDed bits)."""
ip = 0
ab = a & b
while ab > 0:
ip ^= ab & 1
ab >>= 1
return ip
def nonLinearity(t):
"""compute nonlinearity of a binary sequence."""
wt = walshTransform(t)
max_wt = max(abs(w) for w in wt)
return len(t) // 2 - max_wt / 2
def sboxNonlinearity(sbox):
"""calculate min and max nonlinearity for an S-box."""
n = len(sbox)
nlv = [0] * (n - 1)
for c in range(1, n): # Skip zero combination
t = [binaryInnerProduct(c, sbox[i]) for i in range(n)]
nlv[c - 1] = nonLinearity(t)
return min(nlv), max(nlv)
# Example with a custom S-box
# custom_sbox = [0x3, 0x6, 0x5, 0xC, 0xA, 0x0, 0xE, 0x9, 0xD, 0x8, 0xF, 0x4, 0x7, 0x1, 0xB, 0x2]
# custom_sbox = [0x4a, 0x49, 0xa4, 0x2c, 0xc2, 0x6e, 0xb5, 0x4f, 0x00, 0xbb, 0x8f, 0x75, 0x19, 0x56, 0x86, 0x77, 0x8e, 0xaa, 0x51, 0x01, 0xff, 0x6d, 0xcf, 0x14, 0x60, 0xc0, 0x7c, 0x22, 0xf4, 0xe0, 0xf5, 0x0a, 0x44, 0x1c, 0x6a, 0x32, 0xe4, 0xad, 0xb4, 0xa8, 0xa3, 0xb2, 0x7f, 0xa0, 0x78, 0x80, 0xa2, 0x50, 0x08, 0x95, 0x8d, 0xaf, 0xd1, 0x10, 0xf8, 0xea, 0xd7, 0x0e, 0x5d, 0xd5, 0x43, 0x68, 0x7d, 0x29, 0xfd, 0x69, 0xd8, 0x59, 0x55, 0x97, 0x94, 0x73, 0xd9, 0xf9, 0xfb, 0x82, 0x31, 0xb9, 0x2f, 0x89, 0x76, 0xe8, 0x99, 0x88, 0x21, 0x7e, 0x2d, 0x92, 0xee, 0xa1, 0xae, 0xd0, 0x36, 0x42, 0x1a, 0x20, 0x03, 0x34, 0x2b, 0xcd, 0x66, 0x71, 0xcc, 0x3d, 0xd4, 0x2e, 0x40, 0xe9, 0x23, 0x70, 0x65, 0xcb, 0xb7, 0x33, 0xce, 0x9d, 0x18, 0xeb, 0x61, 0xa6, 0xfa, 0x3c, 0xbe, 0x53, 0xda, 0x9f, 0xd2, 0x90, 0xfc, 0xf1, 0xd3, 0xf0, 0x24, 0x12, 0xac, 0xbd, 0x5e, 0x4c, 0xc4, 0x38, 0xbf, 0x4e, 0xd6, 0xef, 0x3b, 0x8c, 0xc1, 0x2a, 0xb0, 0x4b, 0x06, 0x5f, 0x1e, 0x0c, 0x46, 0x13, 0xdf, 0xdd, 0x85, 0x57, 0xc3, 0xde, 0xe1, 0x8a, 0xbc, 0xc9, 0x9e, 0xca, 0x83, 0x72, 0x6f, 0xa5, 0x79, 0x54, 0x9b, 0x96, 0x93, 0x30, 0xe3, 0x1b, 0x39, 0xe5, 0x1f, 0x47, 0x3a, 0x64, 0xab, 0x9c, 0x63, 0xf2, 0x16, 0xe2, 0xfe, 0x87, 0xf7, 0x04, 0xdc, 0x5c, 0x7b, 0x15, 0x5a, 0xf6, 0x58, 0x6c, 0xc6, 0xb6, 0x5b, 0x1d, 0xb3, 0xc8, 0x25, 0x7a, 0xf3, 0x02, 0x3f, 0x0b, 0x37, 0x41, 0x3e, 0x4d, 0xe7, 0xc5, 0xc7, 0x67, 0x9a, 0x84, 0xb8, 0x0d, 0x6b, 0x91, 0x11, 0x98, 0x8b, 0x35, 0xb1, 0x09, 0xdb, 0x28, 0x81, 0x17, 0x74, 0xed, 0x62, 0xa7, 0xa9, 0x07, 0x0f, 0xba, 0x27, 0x52, 0xe6, 0x05, 0x48, 0x45, 0x26, 0xec]
# custom_sbox = [
# 14, 4, 13, 1, 2, 15, 11, 8, 3, 10, 6, 12, 5, 9, 0, 7,
# 0, 15, 7, 4, 14, 2, 13, 1, 10, 6, 12, 11, 9, 5, 3, 8,
# 4, 1, 14, 8, 13, 6, 2, 11, 15, 12, 9, 7, 3, 10, 5, 0,
# 15, 12, 8, 2, 4, 9, 1, 7, 5, 11, 3, 14, 10, 0, 6, 13
# ];
custom_sbox = [0x63,0x7c,0x77,0x7b,0xf2,0x6b,0x6f,0xc5,
0x30,0x01,0x67,0x2b,0xfe,0xd7,0xab,0x76,
0xca,0x82,0xc9,0x7d,0xfa,0x59,0x47,0xf0,
0xad,0xd4,0xa2,0xaf,0x9c,0xa4,0x72,0xc0,
0xb7,0xfd,0x93,0x26,0x36,0x3f,0xf7,0xcc,
0x34,0xa5,0xe5,0xf1,0x71,0xd8,0x31,0x15,
0x04,0xc7,0x23,0xc3,0x18,0x96,0x05,0x9a,
0x07,0x12,0x80,0xe2,0xeb,0x27,0xb2,0x75,
0x09,0x83,0x2c,0x1a,0x1b,0x6e,0x5a,0xa0,
0x52,0x3b,0xd6,0xb3,0x29,0xe3,0x2f,0x84,
0x53,0xd1,0x00,0xed,0x20,0xfc,0xb1,0x5b,
0x6a,0xcb,0xbe,0x39,0x4a,0x4c,0x58,0xcf,
0xd0,0xef,0xaa,0xfb,0x43,0x4d,0x33,0x85,
0x45,0xf9,0x02,0x7f,0x50,0x3c,0x9f,0xa8,
0x51,0xa3,0x40,0x8f,0x92,0x9d,0x38,0xf5,
0xbc,0xb6,0xda,0x21,0x10,0xff,0xf3,0xd2,
0xcd,0x0c,0x13,0xec,0x5f,0x97,0x44,0x17,
0xc4,0xa7,0x7e,0x3d,0x64,0x5d,0x19,0x73,
0x60,0x81,0x4f,0xdc,0x22,0x2a,0x90,0x88,
0x46,0xee,0xb8,0x14,0xde,0x5e,0x0b,0xdb,
0xe0,0x32,0x3a,0x0a,0x49,0x06,0x24,0x5c,
0xc2,0xd3,0xac,0x62,0x91,0x95,0xe4,0x79,
0xe7,0xc8,0x37,0x6d,0x8d,0xd5,0x4e,0xa9,
0x6c,0x56,0xf4,0xea,0x65,0x7a,0xae,0x08,
0xba,0x78,0x25,0x2e,0x1c,0xa6,0xb4,0xc6,
0xe8,0xdd,0x74,0x1f,0x4b,0xbd,0x8b,0x8a,
0x70,0x3e,0xb5,0x66,0x48,0x03,0xf6,0x0e,
0x61,0x35,0x57,0xb9,0x86,0xc1,0x1d,0x9e,
0xe1,0xf8,0x98,0x11,0x69,0xd9,0x8e,0x94,
0x9b,0x1e,0x87,0xe9,0xce,0x55,0x28,0xdf,
0x8c,0xa1,0x89,0x0d,0xbf,0xe6,0x42,0x68,
0x41,0x99,0x2d,0x0f,0xb0,0x54,0xbb,0x16]
print("custom S-box nonlinearity (min, max) =", sboxNonlinearity(custom_sbox))
Run it via command:
python3 sbox_nonlinearity.py
As you can see, everything is worked perfectly! =^..^=
examples
Twofish uses precomputed S-boxes based on key-dependent transformations. Below is one of its default S-boxes
(from the specification):
{
0xa9, 0x67, 0xb3, 0xe8, 0x04, 0xfd, 0xa3, 0x76, 0x9a, 0x92, 0x80, 0x78, 0xe4, 0xdd, 0xd1, 0x38,
0x0d, 0xc6, 0x35, 0x98, 0x18, 0xf7, 0xec, 0x6c, 0x43, 0x75, 0x37, 0x26, 0xfa, 0x13, 0x94, 0x48,
0xf2, 0xd0, 0x8b, 0x30, 0x84, 0x54, 0xdf, 0x23, 0x19, 0x5b, 0x3d, 0x59, 0xf3, 0xae, 0xa2, 0x82,
0x63, 0x01, 0x83, 0x2e, 0xd9, 0x51, 0x9b, 0x7c, 0xa6, 0xeb, 0xa5, 0xbe, 0x16, 0x0c, 0xe3, 0x61,
0xc0, 0x8c, 0x3a, 0xf5, 0x73, 0x2c, 0x25, 0x0b, 0xbb, 0x4e, 0x89, 0x6b, 0x53, 0x6a, 0xb4, 0xf1,
0xe1, 0xe6, 0xbd, 0x45, 0xe2, 0xf4, 0xb6, 0x66, 0xcc, 0x95, 0x03, 0x56, 0xd4, 0x1c, 0x1e, 0xd7,
0xfb, 0xc3, 0x8e, 0xb5, 0xe9, 0xcf, 0xbf, 0xba, 0xea, 0x77, 0x39, 0xaf, 0x33, 0xc9, 0x62, 0x71,
0x81, 0x79, 0x09, 0xad, 0x24, 0xcd, 0xf9, 0xd8, 0xe5, 0xc5, 0xb9, 0x4d, 0x44, 0x08, 0x86, 0xe7,
0xa1, 0x1d, 0xaa, 0xed, 0x06, 0x70, 0xb2, 0xd2, 0x41, 0x7b, 0xa0, 0x11, 0x31, 0xc2, 0x27, 0x90,
0x20, 0xf6, 0x60, 0xff, 0x96, 0x5c, 0xb1, 0xab, 0x9e, 0x9c, 0x52, 0x1b, 0x5f, 0x93, 0x0a, 0xef,
0x91, 0x85, 0x49, 0xee, 0x2d, 0x4f, 0x8f, 0x3b, 0x47, 0x87, 0x6d, 0x46, 0xd6, 0x3e, 0x69, 0x64,
0x2a, 0xce, 0xcb, 0x2f, 0xfc, 0x97, 0x05, 0x7a, 0xac, 0x7f, 0xd5, 0x1a, 0x4b, 0x0e, 0xa7, 0x5a,
0x28, 0x14, 0x3f, 0x29, 0x88, 0x3c, 0x4c, 0x02, 0xb8, 0xda, 0xb0, 0x17, 0x55, 0x1f, 0x8a, 0x7d,
0x57, 0xc7, 0x8d, 0x74, 0xb7, 0xc4, 0x9f, 0x72, 0x7e, 0x15, 0x22, 0x12, 0x58, 0x07, 0x99, 0x34,
0x6e, 0x50, 0xde, 0x68, 0x65, 0xbc, 0xdb, 0xf8, 0xc8, 0xa8, 0x2b, 0x40, 0xdc, 0xfe, 0x32, 0xa4,
0xca, 0x10, 0x21, 0xf0, 0xd3, 0x5d, 0x0f, 0x00, 0x6f, 0x9d, 0x36, 0x42, 0x4a, 0x5e, 0xc1, 0xe0
}
SM4 (a Chinese block cipher standard) has a 256
-entry S-box
, similar to AES
. Here is the SM4 S-box:
{
0xd6, 0x90, 0xe9, 0xfe, 0xcc, 0xe1, 0x3d, 0xb7, 0x16, 0xb6, 0x14, 0xc2, 0x28, 0xfb, 0x2c, 0x05,
0x2b, 0x67, 0x9a, 0x76, 0x2a, 0xbe, 0x04, 0xc3, 0xaa, 0x44, 0x13, 0x26, 0x49, 0x86, 0x06, 0x99,
0x9c, 0x42, 0x50, 0xf4, 0x91, 0xef, 0x98, 0x7a, 0x33, 0x54, 0x0b, 0x43, 0xed, 0xcf, 0xac, 0x62,
0xe4, 0xb3, 0x1c, 0xa9, 0xc9, 0x08, 0xe8, 0x95, 0x80, 0xdf, 0x94, 0xfa, 0x75, 0x8f, 0x3f, 0xa6,
0x47, 0x07, 0xa7, 0xfc, 0xf3, 0x73, 0x17, 0xba, 0x83, 0x59, 0x3c, 0x19, 0xe6, 0x85, 0x4f, 0xa8,
0x68, 0x6b, 0x81, 0xb2, 0x71, 0x64, 0xda, 0x8b, 0xf8, 0xeb, 0x0f, 0x4b, 0x70, 0x56, 0x9d, 0x35,
0x1e, 0x24, 0x0e, 0x5e, 0x63, 0x58, 0xd1, 0xa2, 0x25, 0x22, 0x7c, 0x3b, 0x01, 0x21, 0x78, 0x87,
0xd4, 0x00, 0x46, 0x57, 0x9f, 0xd3, 0x27, 0x52, 0x4c, 0x36, 0x02, 0xe7, 0xa0, 0xc4, 0xc8, 0x9e,
0xea, 0xbf, 0x8a, 0xd2, 0x40, 0xc7, 0x38, 0xb5, 0xa3, 0xf7, 0xf2, 0xce, 0xf9, 0x61, 0x15, 0xa1,
0xe0, 0xae, 0x5d, 0xa4, 0x9b, 0x34, 0x1a, 0x55, 0xad, 0x93, 0x32, 0x30, 0xf5, 0x8c, 0xb1, 0xe3,
0x1d, 0xf6, 0xe2, 0x2e, 0x82, 0x66, 0xca, 0x60, 0xc0, 0x29, 0x23, 0xab, 0x0d, 0x53, 0x4e, 0x6f,
0xd5, 0xdb, 0x37, 0x45, 0xde, 0xfd, 0x8e, 0x2f, 0x03, 0xff, 0x6a, 0x72, 0x6d, 0x6c, 0x5b, 0x51,
0x8d, 0x1b, 0xaf, 0x92, 0xbb, 0xdd, 0xbc, 0x7f, 0x11, 0xd9, 0x5c, 0x41, 0x1f, 0x10, 0x5a, 0xd8,
0x0a, 0xc1, 0x31, 0x88, 0xa5, 0xcd, 0x7b, 0xbd, 0x2d, 0x74, 0xd0, 0x12, 0xb8, 0xe5, 0xb4, 0xb0,
0x89, 0x69, 0x97, 0x4a, 0x0c, 0x96, 0x77, 0x7e, 0x65, 0xb9, 0xf1, 0x09, 0xc5, 0x6e, 0xc6, 0x84,
0x18, 0xf0, 0x7d, 0xec, 0x3a, 0xdc, 0x4d, 0x20, 0x79, 0xee, 0x5f, 0x3e, 0xd7, 0xcb, 0x39, 0x48
};
Camelia cipher:
unsigned char custom_sbox[256] = {
112, 130, 44, 236, 179, 39, 192, 229, 228, 133, 87, 53, 234, 12, 174, 65,
35, 239, 107, 147, 69, 25, 165, 33, 237, 14, 79, 78, 29, 101, 146, 189,
134, 184, 175, 143, 124, 235, 31, 206, 62, 48, 220, 95, 94, 197, 11, 26,
166, 225, 57, 202, 213, 71, 93, 61, 217, 1, 90, 214, 81, 86, 108, 77,
139, 13, 154, 200, 248, 87, 120, 7, 46, 182, 65, 131, 30, 94, 184, 212,
140, 67, 128, 157, 158, 63, 174, 143, 122, 15, 2, 49, 160, 83, 153, 79,
37, 150, 240, 117, 169, 74, 76, 219, 91, 208, 119, 207, 103, 178, 47, 138,
32, 160, 203, 125, 20, 232, 98, 8, 181, 173, 102, 210, 178, 252, 50, 233,
249, 156, 109, 159, 80, 19, 45, 147, 243, 16, 173, 17, 117, 54, 215, 132,
96, 230, 218, 166, 209, 57, 59, 141, 88, 84, 152, 41, 195, 13, 90, 4,
42, 70, 252, 38, 237, 72, 118, 24, 76, 129, 181, 211, 243, 33, 91, 170,
51, 164, 87, 178, 194, 26, 109, 214, 224, 192, 149, 172, 230, 222, 148, 110,
177, 45, 75, 191, 107, 226, 145, 58, 198, 108, 111, 64, 239, 221, 55, 18,
201, 231, 134, 236, 190, 40, 185, 42, 158, 189, 146, 0, 48, 93, 228, 200,
99, 25, 120, 170, 199, 100, 23, 98, 233, 153, 68, 161, 246, 72, 182, 167,
122, 236, 135, 104, 158, 97, 27, 112, 169, 29, 20, 38, 11, 101, 89, 141
};
minimum and maximum of non-linearity
Minimum nonlinearity: Represents the weakest point of the S-box
. If an S-box
has a low minimum nonlinearity, it means at least one linear function approximates it more closely than others. For example, if the minimum nonlinearity is low (e.g., 4
), the S-box
is weaker in resisting attacks in some specific linear approximations.
Maximum nonlinearity: Represents the strongest deviation from any linear function among all evaluated inputs. This value is typically close to the theoretical maximum for a strong S-box
. A high maximum nonlinearity indicates that the S-box
has strong points that are highly resistant to linear approximations. If the maximum nonlinearity is high (e.g., 112
for a typical S-box
of size 256
, (AES, )), the S-box has good resistance overall.
For an 8-bit
S-box
(e.g., AES
) theoretical maximum nonlinearity is 112. Good S-box
designs often achieve nonlinearity values close to 112
across most inputs, with the minimum nonlinearity not falling significantly below that.
For example, if your S-box
has minimum nonlinearity = 104
, and maximum nonlinearity = 112
, it means that the weakest linear combination of outputs can be approximated with a deviation of 104
, which is still quite strong. The strongest deviation is 112
, showing the S-box
performs well in resisting linear approximations.
conclusion
Minimum nonlinearity shows the S-box
’s worst-case resistance to linear approximations. Maximum nonlinearity shows the best-case deviation from linearity. An ideal cryptographic S-box
has a high minimum and maximum nonlinearity, both close to the theoretical maximum. This ensures consistent resistance across all possible attacks.
I wonder if there is a nonlinearity impact to, for example, entropy and, accordingly, with the level of AV/VirusTotal detection?
In theory: S-boxes
with higher nonlinearity typically produce more uniform ciphertext distributions, increasing entropy. If a weak S-box
is generated, the ciphertext may exhibit patterns that reduce entropy, potentially exposing the payload to statistical attacks or aiding detection by security tools.
Adds variability to each encryption round, making reverse engineering more difficult.
But all this is in theory, and I need to test it in practice.
In the following posts we will consider how this nonlinearity affects AV/VirusTotal detection on payload, traffic encryption or ransomware simulation.
I hope this post is useful for malware researchers, C/C++ programmers, spreads awareness to the blue teamers of this interesting analysis technique, and adds a weapon to the red teamers arsenal.
Walsh Transform: wikipedia
Measuring Boolean Function Nonlinearity by Walsh Transform
Marcin Kontak, Janusz Szmidt: Nonlinearity of the Round Function
Linear cryptanalysis
SM4 cipher
Camellia cipher
Malware and cryptography 1
source code in github
This is a practical case for educational purposes only.
Thanks for your time happy hacking and good bye!
PS. All drawings and screenshots are mine