Skip to content

Instantly share code, notes, and snippets.

@odzhan
Created September 15, 2024 05:56
Show Gist options
  • Save odzhan/f0cb8657060199b93540f710f4883485 to your computer and use it in GitHub Desktop.
Save odzhan/f0cb8657060199b93540f710f4883485 to your computer and use it in GitHub Desktop.
FEAL Block Cipher Implementations from Handbook of Cryptography.
//
// FEAL-8 Block Cipher
//
#include <stdio.h>
#include <stdint.h>
// Define the Sd function as per equation (7.6)
uint8_t Sd(uint8_t x, uint8_t y, uint8_t d) {
uint16_t sum = x + y + d; // Sum could be up to 0x1FE
uint8_t s = sum & 0xFF; // Ignore the carry out of the top bit (mod 256)
// Left rotate the result by 2 bits
return ((s << 2) | (s >> 6)) & 0xFF;
}
// Define S0 and S1 functions
uint8_t S0(uint8_t x, uint8_t y) {
return Sd(x, y, 0);
}
uint8_t S1(uint8_t x, uint8_t y) {
return Sd(x, y, 1);
}
// Implement the fK function as per Table 7.9 for key schedule
uint32_t fK(uint32_t A, uint32_t B) {
// Extract bytes A0, A1, A2, A3 from A
uint8_t A0 = (A >> 24) & 0xFF;
uint8_t A1 = (A >> 16) & 0xFF;
uint8_t A2 = (A >> 8) & 0xFF;
uint8_t A3 = A & 0xFF;
// Extract bytes B0, B1, B2, B3 from B
uint8_t B0 = (B >> 24) & 0xFF;
uint8_t B1 = (B >> 16) & 0xFF;
uint8_t B2 = (B >> 8) & 0xFF;
uint8_t B3 = B & 0xFF;
// Compute intermediate values
uint8_t t1 = A0 ^ A1;
uint8_t t2 = A2 ^ A3;
// Compute U components
uint8_t U1 = S1(t1, t2 ^ B0);
uint8_t U2 = S0(t2, U1 ^ B1);
uint8_t U0 = S0(A0, U1 ^ B2);
uint8_t U3 = S1(A3, U2 ^ B3);
// Combine U0, U1, U2, U3 into a 32-bit word
uint32_t U = ((uint32_t)U0 << 24) | ((uint32_t)U1 << 16) | ((uint32_t)U2 << 8) | U3;
return U;
}
// Implement the f function as per Table 7.9 for encryption
uint32_t f(uint32_t A, uint16_t Y) {
// Extract bytes A0, A1, A2, A3 from A
uint8_t A0 = (A >> 24) & 0xFF;
uint8_t A1 = (A >> 16) & 0xFF;
uint8_t A2 = (A >> 8) & 0xFF;
uint8_t A3 = A & 0xFF;
// Extract bytes Y0, Y1 from Y
uint8_t Y0 = (Y >> 8) & 0xFF;
uint8_t Y1 = Y & 0xFF;
// Compute intermediate values
uint8_t t1 = (A0 ^ A1) ^ Y0;
uint8_t t2 = (A2 ^ A3) ^ Y1;
// Compute output components
uint8_t F1 = S1(t1, t2);
uint8_t F2 = S0(t2, F1);
uint8_t F0 = S0(A0, F1);
uint8_t F3 = S1(A3, F2);
// Combine F0, F1, F2, F3 into a 32-bit word
uint32_t F = ((uint32_t)F0 << 24) | ((uint32_t)F1 << 16) | ((uint32_t)F2 << 8) | F3;
return F;
}
// Key schedule: Compute sixteen 16-bit subkeys Ki from 64-bit key K
void key_schedule(uint64_t K, uint16_t Ki[16]) {
uint32_t U[11]; // U(-2) to U(8), indices 0 to 10
int i;
// Initialize U(-2), U(-1), U(0)
U[0] = 0; // U(-2) = 0
U[1] = (uint32_t)(K >> 32); // U(-1) = k1...k32 (first 32 bits of K)
U[2] = (uint32_t)(K & 0xFFFFFFFF); // U(0) = k33...k64 (last 32 bits of K)
// Key extension loop
for (i = 1; i <= 8; i++) {
uint32_t temp = U[i + 1] ^ U[i - 1]; // U(i−1) ⊕ U(i−3)
U[i + 2] = fK(U[i], temp); // U(i) ← fK(U(i−2), temp)
// Extract U0, U1, U2, U3 from U[i + 2]
uint8_t U0 = (U[i + 2] >> 24) & 0xFF;
uint8_t U1 = (U[i + 2] >> 16) & 0xFF;
uint8_t U2 = (U[i + 2] >> 8) & 0xFF;
uint8_t U3 = U[i + 2] & 0xFF;
// Compute subkeys Ki
Ki[2 * i - 2] = ((uint16_t)U0 << 8) | U1; // K2i−2 = (U0, U1)
Ki[2 * i - 1] = ((uint16_t)U2 << 8) | U3; // K2i−1 = (U2, U3)
}
}
// Encryption function
uint64_t encrypt(uint64_t M, uint16_t Ki[16]) {
uint32_t L[9], R[9];
int i;
// Divide plaintext M into ML and MR
uint32_t ML = (uint32_t)(M >> 32);
uint32_t MR = (uint32_t)(M & 0xFFFFFFFF);
// Step 3: XOR initial subkeys
L[0] = ML ^ (((uint32_t)Ki[8] << 16) | Ki[9]); // (K8, K9)
R[0] = MR ^ (((uint32_t)Ki[10] << 16) | Ki[11]); // (K10, K11)
// Step 4: R0 ← R0⊕L0
R[0] = R[0] ^ L[0];
// Feistel rounds
for (i = 1; i <= 8; i++) {
L[i] = R[i - 1];
uint32_t temp = f(R[i - 1], Ki[i - 1]); // Ki−1
R[i] = L[i - 1] ^ temp;
}
// Step 6: L8 ← L8⊕R8
L[8] = L[8] ^ R[8];
// Step 7: XOR final subkeys
R[8] = R[8] ^ (((uint32_t)Ki[12] << 16) | Ki[13]); // (K12, K13)
L[8] = L[8] ^ (((uint32_t)Ki[14] << 16) | Ki[15]); // (K14, K15)
// Step 8: C ← (R8, L8) (Note the order is exchanged)
uint64_t C = ((uint64_t)R[8] << 32) | L[8];
return C;
}
int main() {
uint64_t K; // 64-bit key input
uint64_t M; // 64-bit plaintext input
uint64_t C; // 64-bit ciphertext output
uint16_t Ki[16]; // 16 subkeys K0 to K15
int i;
// Example 64-bit key and plaintext (you can modify these as needed)
K = 0x0123456789ABCDEFULL;
M = 0ULL;
// Compute subkeys
key_schedule(K, Ki);
// Display subkeys
printf("Subkeys Ki:\n");
for (i = 0; i < 16; i++) {
printf("K%d: %04X\n", i, Ki[i]);
}
printf("\n");
// Encrypt plaintext
C = encrypt(M, Ki);
// Display ciphertext
printf("Plaintext M: %016llX\n", M);
printf("Ciphertext C: %016llX\n", C);
return 0;
}
//
// FEAL-N Block Cipher
//
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
// Define the Sd function as per equation (7.6)
uint8_t Sd(uint8_t x, uint8_t y, uint8_t d) {
uint16_t sum = x + y + d; // Sum could be up to 0x1FE
uint8_t s = sum & 0xFF; // Ignore the carry out of the top bit (mod 256)
// Left rotate the result by 2 bits
return ((s << 2) | (s >> 6)) & 0xFF;
}
// Define S0 and S1 functions
uint8_t S0(uint8_t x, uint8_t y) {
return Sd(x, y, 0);
}
uint8_t S1(uint8_t x, uint8_t y) {
return Sd(x, y, 1);
}
// Implement the fK function as per Table 7.9 for key schedule
uint32_t fK(uint32_t A, uint32_t B) {
// Extract bytes A0, A1, A2, A3 from A
uint8_t A0 = (A >> 24) & 0xFF;
uint8_t A1 = (A >> 16) & 0xFF;
uint8_t A2 = (A >> 8) & 0xFF;
uint8_t A3 = A & 0xFF;
// Extract bytes B0, B1, B2, B3 from B
uint8_t B0 = (B >> 24) & 0xFF;
uint8_t B1 = (B >> 16) & 0xFF;
uint8_t B2 = (B >> 8) & 0xFF;
uint8_t B3 = B & 0xFF;
// Compute intermediate values
uint8_t t1 = A0 ^ A1;
uint8_t t2 = A2 ^ A3;
// Compute U components
uint8_t U1 = S1(t1, t2 ^ B0);
uint8_t U2 = S0(t2, U1 ^ B1);
uint8_t U0 = S0(A0, U1 ^ B2);
uint8_t U3 = S1(A3, U2 ^ B3);
// Combine U0, U1, U2, U3 into a 32-bit word
uint32_t U = ((uint32_t)U0 << 24) | ((uint32_t)U1 << 16) | ((uint32_t)U2 << 8) | U3;
return U;
}
// Implement the f function as per Table 7.9 for encryption
uint32_t f(uint32_t A, uint16_t Y) {
// Extract bytes A0, A1, A2, A3 from A
uint8_t A0 = (A >> 24) & 0xFF;
uint8_t A1 = (A >> 16) & 0xFF;
uint8_t A2 = (A >> 8) & 0xFF;
uint8_t A3 = A & 0xFF;
// Extract bytes Y0, Y1 from Y
uint8_t Y0 = (Y >> 8) & 0xFF;
uint8_t Y1 = Y & 0xFF;
// Compute intermediate values
uint8_t t1 = (A0 ^ A1) ^ Y0;
uint8_t t2 = (A2 ^ A3) ^ Y1;
// Compute output components
uint8_t F1 = S1(t1, t2);
uint8_t F2 = S0(t2, F1);
uint8_t F0 = S0(A0, F1);
uint8_t F3 = S1(A3, F2);
// Combine F0, F1, F2, F3 into a 32-bit word
uint32_t F = ((uint32_t)F0 << 24) | ((uint32_t)F1 << 16) | ((uint32_t)F2 << 8) | F3;
return F;
}
// Key schedule: Compute N + 8 sixteen-bit subkeys Ki from 64-bit key K
void key_schedule(uint64_t K, uint16_t *Ki, int N) {
int total_subkeys = N + 8;
int num_U_values = (N / 2) + 4 + 2; // U(-2) to U((N/2)+3)
uint32_t *U = malloc(num_U_values * sizeof(uint32_t)); // Dynamic allocation for U array
int i;
// Initialize U(-2), U(-1), U(0)
U[0] = 0; // U(-2) = 0
U[1] = (uint32_t)(K >> 32); // U(-1) = k1...k32 (first 32 bits of K)
U[2] = (uint32_t)(K & 0xFFFFFFFF); // U(0) = k33...k64 (last 32 bits of K)
// Key extension loop
for (i = 1; i <= (N / 2) + 4; i++) {
uint32_t temp = U[i + 1] ^ U[i - 1]; // U(i−1) ⊕ U(i−3)
U[i + 2] = fK(U[i], temp); // U(i) ← fK(U(i−2), temp)
// Extract U0, U1, U2, U3 from U[i + 2]
uint8_t U0 = (U[i + 2] >> 24) & 0xFF;
uint8_t U1 = (U[i + 2] >> 16) & 0xFF;
uint8_t U2 = (U[i + 2] >> 8) & 0xFF;
uint8_t U3 = U[i + 2] & 0xFF;
// Compute subkeys Ki
Ki[2 * i - 2] = ((uint16_t)U0 << 8) | U1; // K2i−2 = (U0, U1)
Ki[2 * i - 1] = ((uint16_t)U2 << 8) | U3; // K2i−1 = (U2, U3)
}
free(U);
}
// Encryption function for FEAL-N
uint64_t encrypt(uint64_t M, uint16_t *Ki, int N) {
uint32_t *L = malloc((N + 1) * sizeof(uint32_t));
uint32_t *R = malloc((N + 1) * sizeof(uint32_t));
int i;
// Divide plaintext M into ML and MR
uint32_t ML = (uint32_t)(M >> 32);
uint32_t MR = (uint32_t)(M & 0xFFFFFFFF);
// Step 3: XOR initial subkeys
L[0] = ML ^ (((uint32_t)Ki[N] << 16) | Ki[N + 1]); // (KN, KN+1)
R[0] = MR ^ (((uint32_t)Ki[N + 2] << 16) | Ki[N + 3]); // (KN+2, KN+3)
// Step 4: R0 ← R0⊕L0
R[0] = R[0] ^ L[0];
// Feistel rounds
for (i = 1; i <= N; i++) {
L[i] = R[i - 1];
uint32_t temp = f(R[i - 1], Ki[i - 1]); // Ki−1
R[i] = L[i - 1] ^ temp;
}
// Step 6: L_N ← L_N⊕R_N
L[N] = L[N] ^ R[N];
// Step 7: XOR final subkeys
R[N] = R[N] ^ (((uint32_t)Ki[N + 4] << 16) | Ki[N + 5]); // (KN+4, KN+5)
L[N] = L[N] ^ (((uint32_t)Ki[N + 6] << 16) | Ki[N + 7]); // (KN+6, KN+7)
// Step 8: C ← (R_N, L_N) (Note the order is exchanged)
uint64_t C = ((uint64_t)R[N] << 32) | L[N];
free(L);
free(R);
return C;
}
int main() {
uint64_t K; // 64-bit key input
uint64_t M; // 64-bit plaintext input
uint64_t C; // 64-bit ciphertext output
uint16_t *Ki; // Subkeys Ki
int i;
int N; // Number of rounds
// Set the number of rounds (must be even)
N = 8; // Example: FEAL-16
// Calculate the total number of subkeys needed
int total_subkeys = N + 8;
// Allocate memory for subkeys
Ki = malloc(total_subkeys * sizeof(uint16_t));
// Example 64-bit key and plaintext (you can modify these as needed)
K = 0x0123456789ABCDEFULL;
M = 0ULL;
// Compute subkeys
key_schedule(K, Ki, N);
// Display subkeys
printf("Subkeys Ki:\n");
for (i = 0; i < total_subkeys; i++) {
printf("K%d: %04X\n", i, Ki[i]);
}
printf("\n");
// Encrypt plaintext
C = encrypt(M, Ki, N);
// Display plaintext and ciphertext
printf("Plaintext M: %016llX\n", M);
printf("Ciphertext C: %016llX\n", C);
// Clean up
free(Ki);
return 0;
}
//
// FEAL-NX Block Cipher
//
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
// Define the Sd function as per equation (7.6)
uint8_t Sd(uint8_t x, uint8_t y, uint8_t d) {
uint16_t sum = x + y + d; // Sum could be up to 0x1FE
uint8_t s = sum & 0xFF; // Ignore the carry out of the top bit (mod 256)
// Left rotate the result by 2 bits
return ((s << 2) | (s >> 6)) & 0xFF;
}
// Define S0 and S1 functions
uint8_t S0(uint8_t x, uint8_t y) {
return Sd(x, y, 0);
}
uint8_t S1(uint8_t x, uint8_t y) {
return Sd(x, y, 1);
}
// Implement the fK function as per Table 7.9 for key schedule
uint32_t fK(uint32_t A, uint32_t B) {
// Extract bytes A0, A1, A2, A3 from A
uint8_t A0 = (A >> 24) & 0xFF;
uint8_t A1 = (A >> 16) & 0xFF;
uint8_t A2 = (A >> 8) & 0xFF;
uint8_t A3 = A & 0xFF;
// Extract bytes B0, B1, B2, B3 from B
uint8_t B0 = (B >> 24) & 0xFF;
uint8_t B1 = (B >> 16) & 0xFF;
uint8_t B2 = (B >> 8) & 0xFF;
uint8_t B3 = B & 0xFF;
// Compute intermediate values
uint8_t t1 = A0 ^ A1;
uint8_t t2 = A2 ^ A3;
// Compute U components
uint8_t U1 = S1(t1, t2 ^ B0);
uint8_t U2 = S0(t2, U1 ^ B1);
uint8_t U0 = S0(A0, U1 ^ B2);
uint8_t U3 = S1(A3, U2 ^ B3);
// Combine U0, U1, U2, U3 into a 32-bit word
uint32_t U = ((uint32_t)U0 << 24) | ((uint32_t)U1 << 16) | ((uint32_t)U2 << 8) | U3;
return U;
}
// Implement the f function as per Table 7.9 for encryption
uint32_t f(uint32_t A, uint16_t Y) {
// Extract bytes A0, A1, A2, A3 from A
uint8_t A0 = (A >> 24) & 0xFF;
uint8_t A1 = (A >> 16) & 0xFF;
uint8_t A2 = (A >> 8) & 0xFF;
uint8_t A3 = A & 0xFF;
// Extract bytes Y0, Y1 from Y
uint8_t Y0 = (Y >> 8) & 0xFF;
uint8_t Y1 = Y & 0xFF;
// Compute intermediate values
uint8_t t1 = (A0 ^ A1) ^ Y0;
uint8_t t2 = (A2 ^ A3) ^ Y1;
// Compute output components
uint8_t F1 = S1(t1, t2);
uint8_t F2 = S0(t2, F1);
uint8_t F0 = S0(A0, F1);
uint8_t F3 = S1(A3, F2);
// Combine F0, F1, F2, F3 into a 32-bit word
uint32_t F = ((uint32_t)F0 << 24) | ((uint32_t)F1 << 16) | ((uint32_t)F2 << 8) | F3;
return F;
}
// Key schedule for FEAL-NX: Compute N + 8 sixteen-bit subkeys Ki from 128-bit key K
void key_schedule(uint64_t KL, uint64_t KR, uint16_t *Ki, int N) {
int total_subkeys = N + 8;
int num_U_values = (N / 2) + 4 + 2; // U(-2) to U((N/2)+3)
uint32_t *U = malloc(num_U_values * sizeof(uint32_t)); // Dynamic allocation for U array
int i;
// Split KR into KR1 and KR2
uint32_t KR1 = (uint32_t)(KR >> 32);
uint32_t KR2 = (uint32_t)(KR & 0xFFFFFFFF);
// Initialize U(-2), U(-1), U(0)
U[0] = 0; // U(-2) = 0
U[1] = (uint32_t)(KL >> 32); // U(-1) = KL high 32 bits
U[2] = (uint32_t)(KL & 0xFFFFFFFF); // U(0) = KL low 32 bits
// Key extension loop
for (i = 1; i <= (N / 2) + 4; i++) {
uint32_t Qi;
// Define Qi based on i mod 3
if (i % 3 == 1) {
Qi = KR1 ^ KR2;
} else if (i % 3 == 2) {
Qi = KR1;
} else { // i % 3 == 0
Qi = KR2;
}
uint32_t temp = U[i + 1] ^ U[i - 1] ^ Qi; // Modified with Qi
U[i + 2] = fK(U[i], temp); // U(i) ← fK(U(i−2), temp)
// Extract U0, U1, U2, U3 from U[i + 2]
uint8_t U0 = (U[i + 2] >> 24) & 0xFF;
uint8_t U1 = (U[i + 2] >> 16) & 0xFF;
uint8_t U2 = (U[i + 2] >> 8) & 0xFF;
uint8_t U3 = U[i + 2] & 0xFF;
// Compute subkeys Ki
Ki[2 * i - 2] = ((uint16_t)U0 << 8) | U1; // K2i−2 = (U0, U1)
Ki[2 * i - 1] = ((uint16_t)U2 << 8) | U3; // K2i−1 = (U2, U3)
}
free(U);
}
// Encryption function for FEAL-NX
uint64_t encrypt(uint64_t M, uint16_t *Ki, int N) {
uint32_t *L = malloc((N + 1) * sizeof(uint32_t));
uint32_t *R = malloc((N + 1) * sizeof(uint32_t));
int i;
// Divide plaintext M into ML and MR
uint32_t ML = (uint32_t)(M >> 32);
uint32_t MR = (uint32_t)(M & 0xFFFFFFFF);
// Step 3: XOR initial subkeys
L[0] = ML ^ (((uint32_t)Ki[N] << 16) | Ki[N + 1]); // (KN, KN+1)
R[0] = MR ^ (((uint32_t)Ki[N + 2] << 16) | Ki[N + 3]); // (KN+2, KN+3)
// Step 4: R0 ← R0⊕L0
R[0] = R[0] ^ L[0];
// Feistel rounds
for (i = 1; i <= N; i++) {
L[i] = R[i - 1];
uint32_t temp = f(R[i - 1], Ki[i - 1]); // Ki−1
R[i] = L[i - 1] ^ temp;
}
// Step 6: L_N ← L_N⊕R_N
L[N] = L[N] ^ R[N];
// Step 7: XOR final subkeys
R[N] = R[N] ^ (((uint32_t)Ki[N + 4] << 16) | Ki[N + 5]); // (KN+4, KN+5)
L[N] = L[N] ^ (((uint32_t)Ki[N + 6] << 16) | Ki[N + 7]); // (KN+6, KN+7)
// Step 8: C ← (R_N, L_N) (Note the order is exchanged)
uint64_t C = ((uint64_t)R[N] << 32) | L[N];
free(L);
free(R);
return C;
}
int main() {
uint64_t KL; // Left 64 bits of the 128-bit key
uint64_t KR; // Right 64 bits of the 128-bit key
uint64_t M; // 64-bit plaintext input
uint64_t C; // 64-bit ciphertext output
uint16_t *Ki; // Subkeys Ki
int i;
int N; // Number of rounds
// Set the number of rounds (must be even)
N = 32; // Example: FEAL-NX with N = 16
// Calculate the total number of subkeys needed
int total_subkeys = N + 8;
// Allocate memory for subkeys
Ki = malloc(total_subkeys * sizeof(uint16_t));
// Example 128-bit key and plaintext (you can modify these as needed)
KL = 0x0123456789ABCDEFULL;
KR = 0x0123456789ABCDEFULL;
M = 0;
// Compute subkeys
key_schedule(KL, KR, Ki, N);
// Display subkeys
printf("Subkeys Ki:\n");
for (i = 0; i < total_subkeys; i++) {
printf("K%d: %04X\n", i, Ki[i]);
}
printf("\n");
// Encrypt plaintext
C = encrypt(M, Ki, N);
// Display plaintext and ciphertext
printf("Plaintext M: %016llX\n", M);
printf("Ciphertext C: %016llX\n", C);
// Clean up
free(Ki);
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment