From 58fd6a0f2addf2feb96f529c2d7c917e5ae16690 Mon Sep 17 00:00:00 2001 From: Marcus Date: Fri, 15 Sep 2017 14:19:02 -0700 Subject: [PATCH] Rename to SoCracked. Add attacks on six and seven rounds. Add sodark utility. --- README.md | 56 +++++- socracked.c | 516 +++++++++++++++++++++++++++++++++++++--------------- sodark.c | 145 +++++++++++++++ sodark.h | 241 ++++++++++++++++++++++++ 4 files changed, 806 insertions(+), 152 deletions(-) create mode 100644 sodark.c create mode 100644 sodark.h diff --git a/README.md b/README.md index 9d0ba0f..3defa8b 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,60 @@ # SoCracked -Key cracker for the SoDark algorithm +Performs key-recovery attacks on the SoDark family of ciphers for automatic +link establishment (ALE) in HF radios specified in MIL-STD-188-141. Attacks on +up to five rounds are possible using virtually any two +plaintext-ciphertext-tweak tuples. Attacks on six and seven rounds require +tuples with tweaks that are identical in all but the fifth tweak byte. ## Build ```console -$ gcc -Ofast -march=native socracked.c -o socracked -lpthread +$ gcc -Ofast -march=native -lpthread socracked.c -o socracked +$ gcc -Ofast -march=native sodark.c -o sodark ``` + +## Run + +For attacks on up to five rounds, the plaintext-ciphertext-tweak tuples are +specified on the command line. The following will perform a key recovery +attack on four rounds using the test vectors from the standard: +```console +$ ./socracked 4 keys.txt 54e0cd 987c6d 543bd88000017550 b2a7c5 53eda9 543bd88080017550 +``` +Matching keys are output to `keys.txt`. An optional third tuple can be +specified to reduce the number of keys found. + +For six and seven rounds, which require a special internal differential for the +attack to work, the tuples are read from a text file. The file has three +columns, one each for plaintext, ciphertext, and tweak. For example: +``` +8147ed c131f6 543bd88040017550 +c28b9c c19af6 543bd88000017550 +f31e21 51f614 543bd88040017550 +f32186 b741bd 543bd88040017550 +f3232f 3d5216 543bd88040017550 +f32928 f1a786 543bd88040017550 +f32f5f ceb446 543bd88000017550 + +``` + +The `sodark` utility can be used to perform encryption and decryption with any +number of rounds. For example: +```console +$ ./sodark -3e 4 54e0cd c2284a1ce7be2f 543bd88000017550 +``` + +It can also be used to generate random plaintexts for testing. The following +will generate 100 plaintext-ciphertext-tweak tuples with the common key +`c2284a1ce7be2f` and tweak `543bd88000017550`. + +```console +$ ./sodark -r 3 7 c2284a1ce7be2f 543bd88000017550 100 +``` + +`sodark -h` will display a brief help message. + + +## License + +This project is licensed under the GNU General Public License -- see the [LICENSE](LICENSE) +file for details. diff --git a/socracked.c b/socracked.c index 3bb84c0..0ebe269 100644 --- a/socracked.c +++ b/socracked.c @@ -1,8 +1,9 @@ /* SoCracked - Attacks two, three, four or five rounds of the SoDark algorithm as specified in MIL-STD-188-141 - and recovers all candidate keys in 2^10 - 2^12 time for two rounds, 2^17 time for three rounds, - 2^33 time for four rounds, and 2^49 time for five rounds. + Attacks up to seven rounds of the Lattice/SoDark algorithm as specified in + MIL-STD-188-141 and recovers all candidate keys in time proportional to + 2^9 for two rounds, 2^16 for three rounds, 2^33 for four rounds, + 2^49 for five rounds, 2^46 for six rounds, and 2^46 for seven rounds. Copyright (C) 2016-2017 Marcus Dansarie @@ -21,54 +22,48 @@ #include #include +#include #include #include #include #include #include -/* Lookup tables for the SoDark algorithm s-box. */ -uint8_t g_sbox_dec[256]; -const uint8_t g_sbox_enc[] = {0x9c, 0xf2, 0x14, 0xc1, 0x8e, 0xcb, 0xb2, 0x65, - 0x97, 0x7a, 0x60, 0x17, 0x92, 0xf9, 0x78, 0x41, - 0x07, 0x4c, 0x67, 0x6d, 0x66, 0x4a, 0x30, 0x7d, - 0x53, 0x9d, 0xb5, 0xbc, 0xc3, 0xca, 0xf1, 0x04, - 0x03, 0xec, 0xd0, 0x38, 0xb0, 0xed, 0xad, 0xc4, - 0xdd, 0x56, 0x42, 0xbd, 0xa0, 0xde, 0x1b, 0x81, - 0x55, 0x44, 0x5a, 0xe4, 0x50, 0xdc, 0x43, 0x63, - 0x09, 0x5c, 0x74, 0xcf, 0x0e, 0xab, 0x1d, 0x3d, - 0x6b, 0x02, 0x5d, 0x28, 0xe7, 0xc6, 0xee, 0xb4, - 0xd9, 0x7c, 0x19, 0x3e, 0x5e, 0x6c, 0xd6, 0x6e, - 0x2a, 0x13, 0xa5, 0x08, 0xb9, 0x2d, 0xbb, 0xa2, - 0xd4, 0x96, 0x39, 0xe0, 0xba, 0xd7, 0x82, 0x33, - 0x0d, 0x5f, 0x26, 0x16, 0xfe, 0x22, 0xaf, 0x00, - 0x11, 0xc8, 0x9e, 0x88, 0x8b, 0xa1, 0x7b, 0x87, - 0x27, 0xe6, 0xc7, 0x94, 0xd1, 0x5b, 0x9b, 0xf0, - 0x9f, 0xdb, 0xe1, 0x8d, 0xd2, 0x1f, 0x6a, 0x90, - 0xf4, 0x18, 0x91, 0x59, 0x01, 0xb1, 0xfc, 0x34, - 0x3c, 0x37, 0x47, 0x29, 0xe2, 0x64, 0x69, 0x24, - 0x0a, 0x2f, 0x73, 0x71, 0xa9, 0x84, 0x8c, 0xa8, - 0xa3, 0x3b, 0xe3, 0xe9, 0x58, 0x80, 0xa7, 0xd3, - 0xb7, 0xc2, 0x1c, 0x95, 0x1e, 0x4d, 0x4f, 0x4e, - 0xfb, 0x76, 0xfd, 0x99, 0xc5, 0xc9, 0xe8, 0x2e, - 0x8a, 0xdf, 0xf5, 0x49, 0xf3, 0x6f, 0x8f, 0xe5, - 0xeb, 0xf6, 0x25, 0xd5, 0x31, 0xc0, 0x57, 0x72, - 0xaa, 0x46, 0x68, 0x0b, 0x93, 0x89, 0x83, 0x70, - 0xef, 0xa4, 0x85, 0xf8, 0x0f, 0xb3, 0xac, 0x10, - 0x62, 0xcc, 0x61, 0x40, 0xf7, 0xfa, 0x52, 0x7f, - 0xff, 0x32, 0x45, 0x20, 0x79, 0xce, 0xea, 0xbe, - 0xcd, 0x15, 0x21, 0x23, 0xd8, 0xb6, 0x0c, 0x3f, - 0x54, 0x1a, 0xbf, 0x98, 0x48, 0x3a, 0x75, 0x77, - 0x2b, 0xae, 0x36, 0xda, 0x7e, 0x86, 0x35, 0x51, - 0x05, 0x12, 0xb8, 0xa6, 0x9a, 0x2c, 0x06, 0x4b}; +#include "sodark.h" + +/* A plaintext-ciphertext-tweak tuple. */ +typedef struct { + uint64_t tw; + uint32_t pt; + uint32_t ct; +} tuple_t; + +/* A pair of tuples. */ +typedef struct { + tuple_t t1; + tuple_t t2; +} pair_t; + +/* An array of tuple-pairs, with some associated data. + Used by functions init_pairs, free_pairs, and add_pair. */ +typedef struct { + pair_t *pairs; + int allocsize; + int allocstep; + int num_pairs; +} pairs_t; pthread_mutex_t g_next_lock; pthread_mutex_t g_write_lock; pthread_mutex_t g_threadcount_lock; -uint64_t g_keysfound = 0; -uint32_t g_threadcount = 0; /* Number of working threads. */ -uint32_t g_next = 0; /* Next work unit. (Value of two key bytes.) */ -FILE *g_outfp = NULL; /* Pointer to output file. */ +uint64_t g_keysfound = 0; /* Number of keys found so far. */ +uint32_t g_threadcount = 0; /* Number of working threads. */ +uint32_t g_next = 0; /* Next work unit. */ +FILE *g_outfp = NULL; /* Pointer to output file. */ +pairs_t g_pairs = {NULL, 0, 0, 0}; +tuple_t *g_tuples = NULL; /* Array of tuples. Used in six- and seven-round attacks. */ +int g_num_tuples = 0; /* Number of tuples in the above array. */ +int g_nrounds = 0; /* Number of rounds to attack. Used by crack67. */ /* Known plaintexts, ciphertexts and tweaks. If only two are used g_pt3 = g_tw3 = g_ct3 = -1. */ uint32_t g_pt1 = (uint32_t)-1; @@ -81,74 +76,15 @@ uint32_t g_ct1 = (uint32_t)-1; uint32_t g_ct2 = (uint32_t)-1; uint32_t g_ct3 = (uint32_t)-1; -/* Do one round of encryption with the SoDark algorithm. - pt Plaintext (24 bits). - rkey Round key, i.e. the three key bytes xored with three bytes of tweak. */ -static inline uint32_t enc_one_round(uint32_t pt, uint32_t rkey); - -/* Do one round of decryption with the SoDark algorithm. - ct Ciphertext (24 bits). - rkey Round key, i.e. the three key bytes xored with three bytes of tweak. */ -static inline uint32_t dec_one_round(uint32_t ct, uint32_t rkey); - -/* Do three rounds of encryption with the SoDark algorithm. - rounds Number of rounds (1-8). - pt Plaintext (24 bits). - key Encryption key (56 bits). - tweak Tweak (64 bits). */ -static inline uint32_t encrypt_sodark(uint8_t rounds, uint32_t pt, uint64_t key, uint64_t tweak); - /* Returns the next work unit, i.e. the next value of two key bytes. - A return value of 0x10000 indicates that there are no more work units available and that the - thread should stop. */ -uint32_t get_next(); - -/* The cracking functions. The argument is not used. */ -void *crack4(void *param); -void *crack5(void *param); - -static inline uint32_t enc_one_round(uint32_t pt, uint32_t rkey) { - uint8_t pa = pt >> 16; - uint8_t pb = (pt >> 8) & 0xff; - uint8_t pc = pt & 0xff; - uint8_t ka = rkey >> 16; - uint8_t kc = (rkey >> 8) & 0xff; - uint8_t kb = rkey & 0xff; - uint8_t ca = g_sbox_enc[pa ^ pb ^ ka]; - uint8_t cc = g_sbox_enc[pc ^ pb ^ kc]; - uint8_t cb = g_sbox_enc[ca ^ pb ^ cc ^ kb]; - return (ca << 16) | (cb << 8) | cc; -} - -static inline uint32_t dec_one_round(uint32_t ct, uint32_t rkey) { - uint8_t ca = ct >> 16; - uint8_t cb = (ct >> 8) & 0xff; - uint8_t cc = ct & 0xff; - uint8_t ka = rkey >> 16; - uint8_t kc = (rkey >> 8) & 0xff; - uint8_t kb = rkey & 0xff; - uint8_t pb = g_sbox_dec[cb] ^ ca ^ cc ^ kb; - uint8_t pc = g_sbox_dec[cc] ^ pb ^ kc; - uint8_t pa = g_sbox_dec[ca] ^ pb ^ ka; - return (pa << 16) | (pb << 8) | pc; -} - -static inline uint32_t encrypt_sodark(uint8_t rounds, uint32_t pt, uint64_t key, uint64_t tweak) { - uint32_t ct = pt; - for (uint8_t round = 0; round < rounds; round++) { - uint32_t rkey = (key >> 32) ^ (tweak >> 40); - tweak = (tweak << 24) | (tweak >> 40); - key = ((key << 24) | (key >> 32)) & 0x00ffffffffffffff; - ct = enc_one_round(ct, rkey); - } - return ct; -} + A return value of >= 0x10000 indicates that there are no more work + units available and that the thread should stop. */ uint32_t get_next() { pthread_mutex_lock(&g_next_lock); if (g_next >= 0x10000) { pthread_mutex_unlock(&g_next_lock); - return 0x10000; + return UINT_MAX; } uint32_t ret = g_next; g_next += 1; @@ -216,7 +152,7 @@ void crack2() { if (k41 == k42 && k51 == k52) { uint64_t key = (uint64_t)k1[i] << 48 | (uint64_t)k2[k] << 40 | (uint64_t)k3 << 32 | (uint64_t)k41 << 24 | (uint64_t)k51 << 16 | (uint64_t)k6 << 8; - if (g_pt3 == (uint32_t)-1 || encrypt_sodark(2, g_pt3, key, g_tw3) == g_ct3) { + if (g_pt3 == (uint32_t)-1 || encrypt_sodark_3(2, g_pt3, key, g_tw3) == g_ct3) { fprintf(g_outfp, "%014" PRIx64 "\n", key); g_keysfound += 1; } @@ -305,7 +241,7 @@ void crack3() { if (k41 == k42) { uint64_t key = (uint64_t)k1 << 48 | (uint64_t)k2 << 40 | (uint64_t)k3 << 32 | (uint64_t)k41 << 24 | (uint64_t)k5 << 16 | (uint64_t)k6 << 8 | k7; - if (g_pt3 == (uint32_t)-1 || encrypt_sodark(3, g_pt3, key, g_tw3) == g_ct3) { + if (g_pt3 == (uint32_t)-1 || encrypt_sodark_3(3, g_pt3, key, g_tw3) == g_ct3) { fprintf(g_outfp, "%014" PRIx64 "\n", key); g_keysfound += 1; } @@ -386,8 +322,8 @@ void *crack4(void *param) { const uint8_t k4 = k45 >> 8; const uint8_t k5 = k45 & 0xff; const uint32_t k345 = ((uint32_t)k3 << 16) | k45; - const uint32_t r31 = dec_one_round(g_ct1, k345 ^ r4tw1); - const uint32_t r32 = dec_one_round(g_ct2, k345 ^ r4tw2); + const uint32_t r31 = dec_one_round_3(g_ct1, k345 ^ r4tw1); + const uint32_t r32 = dec_one_round_3(g_ct2, k345 ^ r4tw2); const uint8_t r31a = (r31 >> 16) & 0xff; const uint8_t r31b = (r31 >> 8) & 0xff; const uint8_t r31c = r31 & 0xff; @@ -427,8 +363,8 @@ void *crack4(void *param) { } for (uint16_t k1 = 0; k1 < 256; k1++) { const uint32_t k123 = ((uint32_t)k1 << 16) | ((uint32_t)k2 << 8) | k3; - const uint32_t r11 = enc_one_round(g_pt1, k123 ^ r1tw1); - const uint32_t r12 = enc_one_round(g_pt2, k123 ^ r1tw2); + const uint32_t r11 = enc_one_round_3(g_pt1, k123 ^ r1tw1); + const uint32_t r12 = enc_one_round_3(g_pt2, k123 ^ r1tw2); const uint8_t r11a = r11 >> 16; const uint8_t r11b = (r11 >> 8) & 0xff; const uint8_t r12a = r12 >> 16; @@ -451,9 +387,9 @@ void *crack4(void *param) { if (k11 == k12 && k61 == k62 && k71 == k72) { const uint64_t key = ((uint64_t)k123 << 32) | ((uint64_t)k4) << 24 | ((uint64_t)(next->k5)) << 16 | ((uint64_t)k61) << 8 | k71; - if (encrypt_sodark(4, g_pt1, key, g_tw1) == g_ct1 - && encrypt_sodark(4, g_pt2, key, g_tw2) == g_ct2 - && (g_pt3 == (uint32_t)-1 || encrypt_sodark(4, g_pt3, key, g_tw3) == g_ct3)) { + if (encrypt_sodark_3(4, g_pt1, key, g_tw1) == g_ct1 + && encrypt_sodark_3(4, g_pt2, key, g_tw2) == g_ct2 + && (g_pt3 == (uint32_t)-1 || encrypt_sodark_3(4, g_pt3, key, g_tw3) == g_ct3)) { pthread_mutex_lock(&g_write_lock); fprintf(g_outfp, "%014" PRIx64 "\n", key); g_keysfound += 1; @@ -509,8 +445,8 @@ void *crack5(void *param) { memset(lists, 0, 0x100 * sizeof(struct delta*)); for (uint16_t k2 = 0; k2 < 0x100; k2++) { uint32_t k123 = ((uint32_t)k1 << 16) | (k2 << 8) | k3; - uint32_t v1 = enc_one_round(enc_one_round(g_pt1, k123 ^ r1tw1), k456 ^ r2tw1); - uint32_t v2 = enc_one_round(enc_one_round(g_pt2, k123 ^ r1tw2), k456 ^ r2tw2); + uint32_t v1 = enc_one_round_3(enc_one_round_3(g_pt1, k123 ^ r1tw1), k456 ^ r2tw1); + uint32_t v2 = enc_one_round_3(enc_one_round_3(g_pt2, k123 ^ r1tw2), k456 ^ r2tw2); uint32_t delta = v1 ^ v2; uint8_t addr = delta & 0xff; if (lists[addr] == NULL) { @@ -529,8 +465,8 @@ void *crack5(void *param) { } for (uint16_t k7 = 0; k7 < 0x100; k7++) { uint32_t k671 = ((k456 & 0xff) << 16) | (k7 << 8) | k1; - uint32_t v1 = dec_one_round(dec_one_round(g_ct1, k671 ^ r5tw1), k345 ^ r4tw1); - uint32_t v2 = dec_one_round(dec_one_round(g_ct2, k671 ^ r5tw2), k345 ^ r4tw2); + uint32_t v1 = dec_one_round_3(dec_one_round_3(g_ct1, k671 ^ r5tw1), k345 ^ r4tw1); + uint32_t v2 = dec_one_round_3(dec_one_round_3(g_ct2, k671 ^ r5tw2), k345 ^ r4tw2); uint32_t db = g_sbox_dec[(v1 >> 8) & 0xff]; db ^= g_sbox_dec[(v2 >> 8) & 0xff]; db ^= v1; @@ -553,9 +489,9 @@ void *crack5(void *param) { continue; } const uint64_t key = pkey | k7 | ((uint64_t)next->k2 << 40); - if (encrypt_sodark(5, g_pt1, key, g_tw1) == g_ct1 - && encrypt_sodark(5, g_pt2, key, g_tw2) == g_ct2 - && (g_pt3 == (uint32_t)-1 || encrypt_sodark(5, g_pt3, key, g_tw3) == g_ct3)) { + if (encrypt_sodark_3(5, g_pt1, key, g_tw1) == g_ct1 + && encrypt_sodark_3(5, g_pt2, key, g_tw2) == g_ct2 + && (g_pt3 == (uint32_t)-1 || encrypt_sodark_3(5, g_pt3, key, g_tw3) == g_ct3)) { pthread_mutex_lock(&g_write_lock); fprintf(g_outfp, "%014" PRIx64 "\n", key); g_keysfound += 1; @@ -574,30 +510,153 @@ void *crack5(void *param) { return NULL; } -int main(int argc, char **argv) { - /* Fill lookup table for the inverse s-box. */ - for (uint16_t i = 0; i < 256; i++) { - g_sbox_dec[g_sbox_enc[i]] = i; +void *crack67(void *param) { + (void)(param); /* Silence unused warning. */ + pthread_mutex_lock(&g_threadcount_lock); + uint32_t threadid = g_threadcount++; + pthread_mutex_unlock(&g_threadcount_lock); + + /* Plaintexts. */ + const uint32_t a01 = (g_pt1 >> 16) & 0xff; + const uint32_t a02 = (g_pt2 >> 16) & 0xff; + const uint32_t b01 = (g_pt1 >> 8) & 0xff; + const uint32_t b02 = (g_pt2 >> 8) & 0xff; + const uint32_t c01 = g_pt1 & 0xff; + const uint32_t c02 = g_pt2 & 0xff; + + /* Tweaks. */ + const uint32_t t11 = (g_tw1 >> 56) & 0xff; + const uint32_t t12 = (g_tw2 >> 56) & 0xff; + const uint32_t t21 = (g_tw1 >> 48) & 0xff; + const uint32_t t22 = (g_tw2 >> 48) & 0xff; + const uint32_t t31 = (g_tw1 >> 40) & 0xff; + const uint32_t t32 = (g_tw2 >> 40) & 0xff; + const uint32_t t41 = (g_tw1 >> 32) & 0xff; + const uint32_t t42 = (g_tw2 >> 32) & 0xff; + const uint32_t t51 = (g_tw1 >> 24) & 0xff; + const uint32_t t52 = (g_tw2 >> 24) & 0xff; + const uint32_t t61 = (g_tw1 >> 16) & 0xff; + const uint32_t t62 = (g_tw2 >> 16) & 0xff; + const uint32_t t71 = (g_tw1 >> 8) & 0xff; + const uint32_t t72 = (g_tw2 >> 8) & 0xff; + const uint32_t t81 = g_tw1 & 0xff; + const uint32_t t82 = g_tw2 & 0xff; + + uint32_t k12; + while ((k12 = get_next()) < 0x10000) { + int k1 = k12 >> 8; + int k2 = k12 & 0xff; + const uint32_t a11 = g_sbox_enc[a01 ^ b01 ^ k1 ^ t11]; + const uint32_t a12 = g_sbox_enc[a02 ^ b02 ^ k1 ^ t12]; + const uint32_t c11 = g_sbox_enc[c01 ^ b01 ^ k2 ^ t21]; + const uint32_t c12 = g_sbox_enc[c02 ^ b02 ^ k2 ^ t22]; + for (int k3 = 0; k3 < 0x100; k3++) { + const uint32_t b11 = g_sbox_enc[a11 ^ b01 ^ c11 ^ k3 ^ t31]; + const uint32_t b12 = g_sbox_enc[a12 ^ b02 ^ c12 ^ k3 ^ t32]; + for (int k4 = 0; k4 < 0x100; k4++) { + const uint32_t a21 = g_sbox_enc[a11 ^ b11 ^ k4 ^ t41]; + const uint32_t a22 = g_sbox_enc[a12 ^ b12 ^ k4 ^ t42]; + for (int k5 = 0; k5 < 0x100; k5++) { + const uint32_t c21 = g_sbox_enc[c11 ^ b11 ^ k5 ^ t51]; + const uint32_t c22 = g_sbox_enc[c12 ^ b12 ^ k5 ^ t52]; + for (int k6 = 0; k6 < 0x100; k6++) { + const uint32_t b21 = g_sbox_enc[a21 ^ b11 ^ c21 ^ k6 ^ t61]; + const uint32_t b22 = g_sbox_enc[a22 ^ b12 ^ c22 ^ k6 ^ t62]; + const uint32_t c31 = g_sbox_enc[c21 ^ b21 ^ k1 ^ t81]; + const uint32_t c32 = g_sbox_enc[c22 ^ b22 ^ k1 ^ t82]; + if ((c31 ^ c32) == (t51 ^ t52)) { + uint64_t pkey = ((uint64_t)k1 << 48) | ((uint64_t)k2 << 40) | ((uint64_t)k3 << 32) + | ((uint64_t)k4 << 24) | ((uint64_t)k5 << 16) | ((uint64_t)k6 << 8); + for (int k7 = 0; k7 < 0x100; k7++) { + uint64_t fullkey = pkey | k7; + if (encrypt_sodark_3(g_nrounds, g_pt1, fullkey, g_tw1) == g_ct1 + && encrypt_sodark_3(g_nrounds, g_pt2, fullkey, g_tw2) == g_ct2 + && (g_pt3 == (uint32_t)-1 + || encrypt_sodark_3(g_nrounds, g_pt3, fullkey, g_tw3) == g_ct3)) { + pthread_mutex_lock(&g_write_lock); + fprintf(g_outfp, "%014" PRIx64 "\n", fullkey); + g_keysfound += 1; + pthread_mutex_unlock(&g_write_lock); + } + } + } + } + } + } + } + } + + pthread_mutex_lock(&g_threadcount_lock); + g_threadcount -= 1; + pthread_mutex_unlock(&g_threadcount_lock); + + return NULL; +} + +static bool init_pairs(pairs_t *pairs) { + assert(pairs != NULL); + pairs->allocsize = pairs->allocstep = 100; + pairs->num_pairs = 0; + pairs->pairs = malloc(sizeof(pair_t) * pairs->allocsize); + if (pairs->pairs == NULL) { + pairs->allocsize = 0; + fprintf(stderr, "Memory allocation error on line %d.\n", __LINE__); + return false; + } + return true; +} + +static void free_pairs(pairs_t *pairs) { + assert(pairs != NULL); + free(pairs->pairs); + pairs->pairs = NULL; + pairs->allocsize = 0; + pairs->num_pairs = 0; +} + +static bool add_pair(pairs_t *pairs, pair_t p) { + assert(pairs != NULL); + assert(pairs->allocsize > 0); + assert(pairs->allocstep > 0); + assert(pairs->num_pairs >= 0); + assert(pairs->num_pairs < pairs->allocsize); + pairs->pairs[pairs->num_pairs] = p; + pairs->num_pairs += 1; + if (pairs->num_pairs == pairs->allocsize) { + pairs->allocsize += pairs->allocstep; + pairs->pairs = realloc(pairs->pairs, sizeof(pair_t) * pairs->allocsize); + if (pairs->pairs == NULL) { + fprintf(stderr, "Memory allocation error on line %d.\n", __LINE__); + return false; + } } + return true; +} - assert(enc_one_round(0x54e0cd, 0xc2284a ^ 0x543bd8) == 0xd0721d); - assert(dec_one_round(0xd0721d, 0xc2284a ^ 0x543bd8) == 0x54e0cd); - assert(dec_one_round(dec_one_round(0xd0721d, 0xc2284a ^ 0x543bd8), 0) == 0x2ac222); - assert(encrypt_sodark(3, 0x54e0cd, 0xc2284a1ce7be2f, 0x543bd88000017550) == 0x41db0c); - assert(encrypt_sodark(4, 0x54e0cd, 0xc2284a1ce7be2f, 0x543bd88000017550) == 0x987c6d); +int main(int argc, char **argv) { + create_sodark_dec_sbox(); + + assert(enc_one_round_3(0x54e0cd, 0xc2284a ^ 0x543bd8) == 0xd0721d); + assert(dec_one_round_3(0xd0721d, 0xc2284a ^ 0x543bd8) == 0x54e0cd); + assert(dec_one_round_3(dec_one_round_3(0xd0721d, 0xc2284a ^ 0x543bd8), 0) == 0x2ac222); + assert(encrypt_sodark_3(3, 0x54e0cd, 0xc2284a1ce7be2f, 0x543bd88000017550) == 0x41db0c); + assert(encrypt_sodark_3(4, 0x54e0cd, 0xc2284a1ce7be2f, 0x543bd88000017550) == 0x987c6d); - const char* usagestr = "Usage:\n" + const char* usagestr = "Usage:\n\n" + "2-5 rounds:\n" "%s rounds outfile plaintext1 ciphertext1 tweak1 plaintext2 ciphertext2 " - "tweak2 [plaintext3 ciphertext3 tweak3]\n\n"; + "tweak2 [plaintext3 ciphertext3 tweak3]\n\n" + "6-7 rounds:\n" + "%s rounds outfile infile\n\n"; - if (argc != 9 && argc != 12) { - printf(usagestr, argv[0]); + if (argc != 4 && argc != 9 && argc != 12) { + printf(usagestr, argv[0], argv[0]); return 1; } uint32_t nrounds = atoi(argv[1]); - if (nrounds < 2 || nrounds > 5) { - fprintf(stderr, "Bad number of rounds. Only 2, 3, 4, and 5 rounds are supported.\n"); + if (nrounds < 2 || nrounds > 8) { + fprintf(stderr, "Bad number of rounds. Only 2 - 8 rounds are supported.\n"); return 1; } @@ -607,21 +666,156 @@ int main(int argc, char **argv) { return 1; } - g_pt1 = strtoul(argv[3], NULL, 16); - g_ct1 = strtoul(argv[4], NULL, 16); - g_tw1 = strtoull(argv[5], NULL, 16); - g_pt2 = strtoul(argv[6], NULL, 16); - g_ct2 = strtoul(argv[7], NULL, 16); - g_tw2 = strtoull(argv[8], NULL, 16); - - printf("PT1: %06" PRIx32 " CT1: %06" PRIx32 " TW1: %016" PRIx64 "\n", g_pt1, g_ct1, g_tw1); - printf("PT2: %06" PRIx32 " CT2: %06" PRIx32 " TW2: %016" PRIx64 "\n", g_pt2, g_ct2, g_tw2); - if (argc == 12) { - g_pt3 = strtoul(argv[9], NULL, 16); - g_ct3 = strtoul(argv[10], NULL, 16); - g_tw3 = strtoull(argv[11], NULL, 16); - printf("PT3: %06" PRIx32 " CT3: %06" PRIx32 " TW3: %016" PRIx64 "\n", g_pt3, g_ct3, g_tw3); - } + if (nrounds <= 5) { + if (argc != 9 && argc != 12) { + printf(usagestr, argv[0], argv[0]); + fclose(g_outfp); + return 1; + } + g_pt1 = strtoul(argv[3], NULL, 16); + g_ct1 = strtoul(argv[4], NULL, 16); + g_tw1 = strtoull(argv[5], NULL, 16); + g_pt2 = strtoul(argv[6], NULL, 16); + g_ct2 = strtoul(argv[7], NULL, 16); + g_tw2 = strtoull(argv[8], NULL, 16); + + printf("PT1: %06" PRIx32 " CT1: %06" PRIx32 " TW1: %016" PRIx64 "\n", g_pt1, g_ct1, g_tw1); + printf("PT2: %06" PRIx32 " CT2: %06" PRIx32 " TW2: %016" PRIx64 "\n", g_pt2, g_ct2, g_tw2); + if (argc == 12) { + g_pt3 = strtoul(argv[9], NULL, 16); + g_ct3 = strtoul(argv[10], NULL, 16); + g_tw3 = strtoull(argv[11], NULL, 16); + printf("PT3: %06" PRIx32 " CT3: %06" PRIx32 " TW3: %016" PRIx64 "\n", g_pt3, g_ct3, g_tw3); + } + } else if (nrounds >= 6 && nrounds <= 7) { + if (argc != 4 && argc != 12) { + printf(usagestr, argv[0], argv[0]); + fclose(g_outfp); + return 1; + } + FILE *infp = fopen(argv[3], "r"); + if (infp == NULL) { + fprintf(stderr, "Could not open input file for reading.\n"); + fclose(g_outfp); + return 1; + } + printf("Reading input file... "); + fflush(stdout); + + const int allocstep = 1000; + int allocsize = allocstep; + g_tuples = malloc(sizeof(tuple_t) * allocstep); + if (g_tuples == NULL) { + fprintf(stderr, "Memory allocation error on line %d.\n", __LINE__); + fclose(g_outfp); + fclose(infp); + return 1; + } + + while (!feof(infp)) { + if (fscanf(infp, "%06x %06x %016" PRIx64 "\n", &g_tuples[g_num_tuples].pt, + &g_tuples[g_num_tuples].ct, &g_tuples[g_num_tuples].tw) == 3) { + g_num_tuples += 1; + if (g_num_tuples == allocsize) { + allocsize += allocstep; + g_tuples = realloc(g_tuples, sizeof(tuple_t) * allocsize); + if (g_tuples == NULL) { + fprintf(stderr, "Memory allocation error.\n"); + fclose(g_outfp); + fclose(infp); + return 1; + } + } + } else { + int c; + while ((c = fgetc(infp)) != '\n' && c != EOF) { + /* Empty. */ + } + } + } + fclose(infp); + infp = NULL; + printf("%d tuples loaded.\n", g_num_tuples); + printf("Filtering pairs... "); + fflush(stdout); + + if (!init_pairs(&g_pairs)) { + free(g_tuples); + fclose(g_outfp); + return 1; + } + + for (int i = 0; i < g_num_tuples; i++) { + for (int k = i + 1; k < g_num_tuples; k++) { + uint64_t delta_tw = g_tuples[i].tw ^ g_tuples[k].tw; + if (delta_tw & 0xffffffff00ffffffL + || ((delta_tw >> 24) & 0xff) == 0) { + continue; + } + if (nrounds == 6) { + if (g_tuples[i].ct == g_tuples[k].ct) { + pair_t p = {g_tuples[i], g_tuples[k]}; + if (!add_pair(&g_pairs, p)) { + free(g_tuples); + fclose(g_outfp); + return 1; + } + } + } else if (nrounds == 7) { + if (((g_tuples[i].ct ^ g_tuples[k].ct) & 0xff00ff) == 0) { + uint8_t a1 = g_tuples[i].ct >> 16; + uint8_t a2 = g_tuples[k].ct >> 16; + uint8_t b1 = (g_tuples[i].ct >> 8) & 0xff; + uint8_t b2 = (g_tuples[k].ct >> 8) & 0xff; + uint8_t c1 = g_tuples[i].ct & 0xff; + uint8_t c2 = g_tuples[k].ct & 0xff; + uint8_t t1 = (g_tuples[i].tw >> 24) & 0xff; + uint8_t t2 = (g_tuples[k].tw >> 24) & 0xff; + uint8_t dbh = g_sbox_dec[b1] ^ a1 ^ c1 ^ t1 ^ g_sbox_dec[b2] ^ a2 ^ c2 ^ t2; + if (dbh == 0) { + pair_t p = {g_tuples[i], g_tuples[k]}; + if (!add_pair(&g_pairs, p)) { + free(g_tuples); + fclose(g_outfp); + return 1; + } + } + } + } else { + assert(0); + } + } + } + printf("%d potential pairs found.\n", g_pairs.num_pairs); + if (g_pairs.num_pairs == 0) { + free(g_tuples); + fclose(g_outfp); + return 0; + } + printf("Only one pair needed. Using first pair.\n"); + g_pt1 = g_pairs.pairs[0].t1.pt; + g_pt2 = g_pairs.pairs[0].t2.pt; + g_ct1 = g_pairs.pairs[0].t1.ct; + g_ct2 = g_pairs.pairs[0].t2.ct; + g_tw1 = g_pairs.pairs[0].t1.tw; + g_tw2 = g_pairs.pairs[0].t2.tw; + + for (int i = 0; i < g_num_tuples; i++) { + if (!((g_tuples[i].pt == g_pt1 && g_tuples[i].ct == g_ct1 && g_tuples[i].tw == g_tw1) + || (g_tuples[i].pt == g_pt2 && g_tuples[i].ct == g_ct2 && g_tuples[i].tw == g_tw2))) { + g_pt3 = g_tuples[i].pt; + g_ct3 = g_tuples[i].ct; + g_tw3 = g_tuples[i].tw; + break; + } + } + + printf("Tuple 1: %06x %06x %016" PRIx64 "\n", g_pt1, g_ct1, g_tw1); + printf("Tuple 2: %06x %06x %016" PRIx64 "\n", g_pt2, g_ct2, g_tw2); + if (g_pt3 != (uint32_t)-1) { + printf("Tuple 3: %06x %06x %016" PRIx64 "\n", g_pt3, g_ct3, g_tw3); + } + } /* nrounds >= 6 && nrounds <= 7 */ void *(*crack_func)(void*) = NULL; switch (nrounds) { @@ -639,7 +833,13 @@ int main(int argc, char **argv) { case 5: crack_func = crack5; break; + case 6: + case 7: + crack_func = crack67; + g_nrounds = nrounds; + break; default: + fprintf(stderr, "Error: %d\n", nrounds); assert(0); } @@ -648,6 +848,10 @@ int main(int argc, char **argv) { || pthread_mutex_init(&g_write_lock, NULL) != 0) { fprintf(stderr, "Mutex init failed.\n"); fclose(g_outfp); + free(g_tuples); + if (g_pairs.pairs != NULL) { + free_pairs(&g_pairs); + } return 1; } @@ -655,7 +859,15 @@ int main(int argc, char **argv) { uint32_t numproc = sysconf(_SC_NPROCESSORS_ONLN); pthread_t thread_id[numproc]; for (uint32_t i = 0; i < numproc; i++) { - pthread_create(&(thread_id[i]), NULL, crack_func, NULL); + if (pthread_create(&(thread_id[i]), NULL, crack_func, NULL) != 0) { + fprintf(stderr, "Error returned from pthread_create. i=%d. numproc=%d\n", i, numproc); + fclose(g_outfp); + free(g_tuples); + if (g_pairs.pairs != NULL) { + free_pairs(&g_pairs); + } + return 1; + } } /* Wait for completion and print progress bar. */ @@ -683,6 +895,10 @@ int main(int argc, char **argv) { pthread_mutex_destroy(&g_threadcount_lock); pthread_mutex_destroy(&g_write_lock); fclose(g_outfp); + free(g_tuples); + if (g_pairs.pairs != NULL) { + free_pairs(&g_pairs); + } printf("\n"); return 0; diff --git a/sodark.c b/sodark.c new file mode 100644 index 0000000..e595994 --- /dev/null +++ b/sodark.c @@ -0,0 +1,145 @@ +/* sodark + + Utility for encryption and decryption with the SoDark family of ciphers. + + Copyright (C) 2017 Marcus Dansarie + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +#include +#include +#include +#include +#include + +#include "sodark.h" + +void printusage(char *name) { + fprintf(stderr, "Usage:\n"); + fprintf(stderr, "%s mode rounds pt/ct key tweak\n", name); + fprintf(stderr, " -3d SoDark-3 decryption\n"); + fprintf(stderr, " -3e SoDark-3 encryption\n"); + fprintf(stderr, " -6d SoDark-6 decryption\n"); + fprintf(stderr, " -6e SoDark-6 encryption\n"); + fprintf(stderr, "%s -r {3|6} rounds key tweak ntuples\n", name); + fprintf(stderr, " Generate ntuples random tuples.\n"); +} + +int main(int argc, char **argv) { + create_sodark_dec_sbox(); + + assert(enc_one_round_3(0x54e0cd, 0xc2284a ^ 0x543bd8) == 0xd0721d); + assert(dec_one_round_3(0xd0721d, 0xc2284a ^ 0x543bd8) == 0x54e0cd); + assert(dec_one_round_3(dec_one_round_3(0xd0721d, 0xc2284a ^ 0x543bd8), 0) == 0x2ac222); + assert(dec_one_round_6(enc_one_round_6(0x1234567890ab, 0x6d7dddd48390), 0x6d7dddd48390) + == 0x1234567890ab); + assert(encrypt_sodark_3(3, 0x54e0cd, 0xc2284a1ce7be2f, 0x543bd88000017550) == 0x41db0c); + assert(encrypt_sodark_3(4, 0x54e0cd, 0xc2284a1ce7be2f, 0x543bd88000017550) == 0x987c6d); + assert(decrypt_sodark_3(1, 0xd0721d, 0xc2284a1ce7be2f, 0x543bd88000017550) == 0x54e0cd); + assert(decrypt_sodark_3(3, 0x41db0c, 0xc2284a1ce7be2f, 0x543bd88000017550) == 0x54e0cd); + assert(decrypt_sodark_3(4, 0x987c6d, 0xc2284a1ce7be2f, 0x543bd88000017550) == 0x54e0cd); + assert(decrypt_sodark_6(1, encrypt_sodark_6(1, 0xdeafcafebabe, 0xc2284a1ce7be2f, + 0x543bd88000017550), 0xc2284a1ce7be2f, 0x543bd88000017550) == 0xdeafcafebabe); + + if (argc == 6) { + uint32_t rounds = atoi(argv[2]); + uint64_t pt_ct = strtoull(argv[3], NULL, 16); + uint64_t key = strtoull(argv[4], NULL, 16); + uint64_t tweak = strtoull(argv[5], NULL, 16); + + if (rounds <= 0) { + fprintf(stderr, "Bad number of rounds.\n\n"); + printusage(argv[0]); + return 1; + } + + printf("Rounds: %d\n", rounds); + printf("PT/CT: %012" PRIx64 "\n", pt_ct); + printf("Key: %014" PRIx64 "\n", key); + printf("Tweak: %016" PRIx64 "\n", tweak); + + if (strcmp(argv[1], "-3d") == 0) { + printf("CT: %06" PRIx32 "\n", decrypt_sodark_3(rounds, (uint32_t)pt_ct, key, tweak)); + } else if (strcmp(argv[1], "-3e") == 0) { + printf("CT: %06" PRIx32 "\n", encrypt_sodark_3(rounds, (uint32_t)pt_ct, key, tweak)); + } else if (strcmp(argv[1], "-6d") == 0) { + printf("CT: %012" PRIx64 "\n", decrypt_sodark_6(rounds, pt_ct, key, tweak)); + } else if (strcmp(argv[1], "-6e") == 0) { + printf("CT: %012" PRIx64 "\n", encrypt_sodark_6(rounds, pt_ct, key, tweak)); + } else { + fprintf(stderr, "Bad mode.\n\n"); + printusage(argv[0]); + return 1; + } + } else if (argc == 7) { + if (strcmp(argv[1], "-r") != 0) { + printusage(argv[0]); + return 1; + } + uint32_t rounds = atoi(argv[3]); + uint64_t key = strtoull(argv[4], NULL, 16); + uint64_t tweak = strtoull(argv[5], NULL, 16); + uint32_t tuples = atoi(argv[6]); + + FILE *randp = fopen("/dev/urandom", "r"); + if (randp == NULL) { + fprintf(stderr, "Error when opening /dev/urandom.\n"); + return 1; + } + + if (strcmp(argv[2], "3") == 0) { + fprintf(stderr, "Generating %d random tuples with SoDark-3.\n", tuples); + fprintf(stderr, "Rounds: %d\n", rounds); + fprintf(stderr, "Key: %014" PRIx64 "\n", key); + fprintf(stderr, "Tweak: %016" PRIx64 "\n", tweak); + uint32_t pt; + for (int i = 0; i < tuples; i++) { + if (fread(&pt, sizeof(uint32_t), 1, randp) != 1) { + fprintf(stderr, "Error when reading from /dev/urandom.\n"); + fclose(randp); + return 1; + } + pt &= 0xffffff; + uint32_t ct = encrypt_sodark_3(rounds, pt, key, tweak); + printf("%06x %06x %016" PRIx64 "\n", pt, ct, tweak); + } + } else if (strcmp(argv[2], "6") == 0) { + fprintf(stderr, "Generating %d random tuples with SoDark-6.\n", tuples); + fprintf(stderr, "Rounds: %d\n", rounds); + fprintf(stderr, "Key: %014" PRIx64 "\n", key); + fprintf(stderr, "Tweak: %016" PRIx64 "\n", tweak); + uint64_t pt; + for (int i = 0; i < tuples; i++) { + if (fread(&pt, sizeof(uint32_t), 1, randp) != 1) { + fprintf(stderr, "Error when reading from /dev/urandom.\n"); + fclose(randp); + return 1; + } + pt &= 0xffffffffffffL; + uint64_t ct = encrypt_sodark_6(rounds, pt, key, tweak); + printf("%012" PRIx64 " %012" PRIx64 " %016" PRIx64 "\n", pt, ct, tweak); + } + } else { + printusage(argv[0]); + fclose(randp); + return 1; + } + fclose(randp); + } else { + printusage(argv[0]); + return 1; + } + + return 0; +} diff --git a/sodark.h b/sodark.h new file mode 100644 index 0000000..63f2493 --- /dev/null +++ b/sodark.h @@ -0,0 +1,241 @@ +/* sodark.h + + Copyright (C) 2016-2017 Marcus Dansarie + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +#ifndef _SODARK_H_ +#define _SODARK_H_ + +#include +#include + +/* Lookup tables for the SoDark s-box. */ +bool g_sbox_dec_init = false; /* Set to true by create_sodark_dec_sbox. */ +uint32_t g_sbox_dec[256]; +const uint32_t g_sbox_enc[] = {0x9c, 0xf2, 0x14, 0xc1, 0x8e, 0xcb, 0xb2, 0x65, + 0x97, 0x7a, 0x60, 0x17, 0x92, 0xf9, 0x78, 0x41, + 0x07, 0x4c, 0x67, 0x6d, 0x66, 0x4a, 0x30, 0x7d, + 0x53, 0x9d, 0xb5, 0xbc, 0xc3, 0xca, 0xf1, 0x04, + 0x03, 0xec, 0xd0, 0x38, 0xb0, 0xed, 0xad, 0xc4, + 0xdd, 0x56, 0x42, 0xbd, 0xa0, 0xde, 0x1b, 0x81, + 0x55, 0x44, 0x5a, 0xe4, 0x50, 0xdc, 0x43, 0x63, + 0x09, 0x5c, 0x74, 0xcf, 0x0e, 0xab, 0x1d, 0x3d, + 0x6b, 0x02, 0x5d, 0x28, 0xe7, 0xc6, 0xee, 0xb4, + 0xd9, 0x7c, 0x19, 0x3e, 0x5e, 0x6c, 0xd6, 0x6e, + 0x2a, 0x13, 0xa5, 0x08, 0xb9, 0x2d, 0xbb, 0xa2, + 0xd4, 0x96, 0x39, 0xe0, 0xba, 0xd7, 0x82, 0x33, + 0x0d, 0x5f, 0x26, 0x16, 0xfe, 0x22, 0xaf, 0x00, + 0x11, 0xc8, 0x9e, 0x88, 0x8b, 0xa1, 0x7b, 0x87, + 0x27, 0xe6, 0xc7, 0x94, 0xd1, 0x5b, 0x9b, 0xf0, + 0x9f, 0xdb, 0xe1, 0x8d, 0xd2, 0x1f, 0x6a, 0x90, + 0xf4, 0x18, 0x91, 0x59, 0x01, 0xb1, 0xfc, 0x34, + 0x3c, 0x37, 0x47, 0x29, 0xe2, 0x64, 0x69, 0x24, + 0x0a, 0x2f, 0x73, 0x71, 0xa9, 0x84, 0x8c, 0xa8, + 0xa3, 0x3b, 0xe3, 0xe9, 0x58, 0x80, 0xa7, 0xd3, + 0xb7, 0xc2, 0x1c, 0x95, 0x1e, 0x4d, 0x4f, 0x4e, + 0xfb, 0x76, 0xfd, 0x99, 0xc5, 0xc9, 0xe8, 0x2e, + 0x8a, 0xdf, 0xf5, 0x49, 0xf3, 0x6f, 0x8f, 0xe5, + 0xeb, 0xf6, 0x25, 0xd5, 0x31, 0xc0, 0x57, 0x72, + 0xaa, 0x46, 0x68, 0x0b, 0x93, 0x89, 0x83, 0x70, + 0xef, 0xa4, 0x85, 0xf8, 0x0f, 0xb3, 0xac, 0x10, + 0x62, 0xcc, 0x61, 0x40, 0xf7, 0xfa, 0x52, 0x7f, + 0xff, 0x32, 0x45, 0x20, 0x79, 0xce, 0xea, 0xbe, + 0xcd, 0x15, 0x21, 0x23, 0xd8, 0xb6, 0x0c, 0x3f, + 0x54, 0x1a, 0xbf, 0x98, 0x48, 0x3a, 0x75, 0x77, + 0x2b, 0xae, 0x36, 0xda, 0x7e, 0x86, 0x35, 0x51, + 0x05, 0x12, 0xb8, 0xa6, 0x9a, 0x2c, 0x06, 0x4b}; + +/* Initializes the SoDark decryption S-box. */ +static inline void create_sodark_dec_sbox() { + /* Fill lookup table for the inverse s-box. */ + for (uint16_t i = 0; i < 256; i++) { + g_sbox_dec[g_sbox_enc[i]] = i; + } + g_sbox_dec_init = true; +} + +/* Do one round of encryption with the SoDark-3 algorithm. + pt Plaintext (24 bits). + rkey Round key, i.e. three key bytes xored with three bytes of tweak. */ +static inline uint32_t enc_one_round_3(uint32_t pt, uint32_t rkey) { + uint32_t pa = pt >> 16; + uint32_t pb = (pt >> 8) & 0xff; + uint32_t pc = pt & 0xff; + uint32_t ka = rkey >> 16; + uint32_t kc = (rkey >> 8) & 0xff; + uint32_t kb = rkey & 0xff; + uint32_t ca = g_sbox_enc[pa ^ pb ^ ka]; + uint32_t cc = g_sbox_enc[pc ^ pb ^ kc]; + uint32_t cb = g_sbox_enc[ca ^ pb ^ cc ^ kb]; + return (ca << 16) | (cb << 8) | cc; +} + +/* Do one round of decryption with the SoDark-3 algorithm. + ct Ciphertext (24 bits). + rkey Round key, i.e. three key bytes xored with three bytes of tweak. */ +static inline uint32_t dec_one_round_3(uint32_t ct, uint32_t rkey) { + if (!g_sbox_dec_init) { + create_sodark_dec_sbox(); + } + uint32_t ca = ct >> 16; + uint32_t cb = (ct >> 8) & 0xff; + uint32_t cc = ct & 0xff; + uint32_t ka = rkey >> 16; + uint32_t kc = (rkey >> 8) & 0xff; + uint32_t kb = rkey & 0xff; + uint32_t pb = g_sbox_dec[cb] ^ ca ^ cc ^ kb; + uint32_t pc = g_sbox_dec[cc] ^ pb ^ kc; + uint32_t pa = g_sbox_dec[ca] ^ pb ^ ka; + return (pa << 16) | (pb << 8) | pc; +} + +/* Do one round of encryption with the SoDark-6 algorithm. + pt Plaintext (48 bits). + rkey Round key, i.e. six key bytes xored with six bytes of tweak. */ +static inline uint64_t enc_one_round_6(uint64_t pt, uint64_t rkey) { + uint64_t pa = pt >> 40; + uint64_t pb = (pt >> 32) & 0xff; + uint64_t pc = (pt >> 24) & 0xff; + uint64_t pd = (pt >> 16) & 0xff; + uint64_t pe = (pt >> 8) & 0xff; + uint64_t pf = pt & 0xff; + uint64_t ka = rkey >> 40; + uint64_t kc = (rkey >> 32) & 0xff; + uint64_t ke = (rkey >> 24) & 0xff; + uint64_t kb = (rkey >> 16) & 0xff; + uint64_t kd = (rkey >> 8) & 0xff; + uint64_t kf = rkey & 0xff; + uint64_t ca = g_sbox_enc[pa ^ pb ^ pf ^ ka]; + uint64_t cc = g_sbox_enc[pb ^ pc ^ pd ^ kc]; + uint64_t ce = g_sbox_enc[pd ^ pe ^ pf ^ ke]; + uint64_t cb = g_sbox_enc[ca ^ pb ^ cc ^ kb]; + uint64_t cd = g_sbox_enc[cc ^ pd ^ ce ^ kd]; + uint64_t cf = g_sbox_enc[ca ^ pf ^ ce ^ kf]; + + return (ca << 40) | (cb << 32) | (cc << 24) | (cd << 16) | (ce << 8) | cf; +} + +/* Do one round of decryption with the SoDark-6 algorithm. + ct Ciphertext (48 bits). + rkey Round key, i.e. six key bytes xored with six bytes of tweak. */ +static inline uint64_t dec_one_round_6(uint64_t ct, uint64_t rkey) { + if (!g_sbox_dec_init) { + create_sodark_dec_sbox(); + } + uint64_t ca = ct >> 40; + uint64_t cb = (ct >> 32) & 0xff; + uint64_t cc = (ct >> 24) & 0xff; + uint64_t cd = (ct >> 16) & 0xff; + uint64_t ce = (ct >> 8) & 0xff; + uint64_t cf = ct & 0xff; + uint64_t ka = rkey >> 40; + uint64_t kc = (rkey >> 32) & 0xff; + uint64_t ke = (rkey >> 24) & 0xff; + uint64_t kb = (rkey >> 16) & 0xff; + uint64_t kd = (rkey >> 8) & 0xff; + uint64_t kf = rkey & 0xff; + uint64_t pb = g_sbox_dec[cb] ^ ca ^ cc ^ kb; + uint64_t pd = g_sbox_dec[cd] ^ cc ^ ce ^ kd; + uint64_t pf = g_sbox_dec[cf] ^ ca ^ ce ^ kf; + uint64_t pa = g_sbox_dec[ca] ^ pb ^ pf ^ ka; + uint64_t pc = g_sbox_dec[cc] ^ pb ^ pd ^ kc; + uint64_t pe = g_sbox_dec[ce] ^ pd ^ pf ^ ke; + + return (pa << 40) | (pb << 32) | (pc << 24) | (pd << 16) | (pe << 8) | pf; +} + +/* Encrypt using the the Sodark-3 algorithm. + rounds Number of rounds. + pt Plaintext (24 bits). + key Encryption key (56 bits). + tweak Tweak (64 bits). */ +static inline uint32_t encrypt_sodark_3(uint32_t rounds, uint32_t pt, uint64_t key, + uint64_t tweak) { + uint32_t ct = pt; + for (uint32_t round = 0; round < rounds; round++) { + uint32_t rkey = ((key >> 32) ^ (tweak >> 40)) & 0xffffff; + tweak = (tweak << 24) | (tweak >> 40); + key = ((key << 24) | (key >> 32)) & 0xffffffffffffffL; + ct = enc_one_round_3(ct, rkey); + } + return ct; +} + +/* Decrypt using the the Sodark-3 algorithm. + rounds Number of rounds. + ct Ciphertext (24 bits). + key Encryption key (56 bits). + tweak Tweak (64 bits). */ +static inline uint32_t decrypt_sodark_3(uint32_t rounds, uint32_t ct, uint64_t key, + uint64_t tweak) { + if (!g_sbox_dec_init) { + create_sodark_dec_sbox(); + } + uint32_t tshift = (24 * (rounds - 1)) % 64; + uint32_t kshift = (24 * (rounds - 1)) % 56; + tweak = (tweak >> (64 - tshift)) | (tweak << tshift); + key = ((key >> (56 - kshift)) | (key << kshift)) & 0xffffffffffffffL; + uint32_t pt = ct; + for (uint32_t round = 0; round < rounds; round++) { + uint32_t rkey = ((key >> 32) ^ (tweak >> 40)) & 0xffffff; + tweak = (tweak >> 24) | (tweak << 40); + key = ((key >> 24) | (key << 32)) & 0xffffffffffffffL; + pt = dec_one_round_3(pt, rkey); + } + return pt; +} + +/* Encrypt using the the Sodark-6 algorithm. + rounds Number of rounds. + pt Plaintext (48 bits). + key Encryption key (56 bits). + tweak Tweak (64 bits). */ +static inline uint64_t encrypt_sodark_6(uint32_t rounds, uint64_t pt, uint64_t key, + uint64_t tweak) { + uint64_t ct = pt; + for (uint32_t round = 0; round < rounds; round++) { + uint64_t rkey = ((key >> 8) ^ (tweak >> 16)) % 0xffffffffffffL; + tweak = (tweak << 48) | (tweak >> 16); + key = ((key << 48) | (key >> 8)) & 0xffffffffffffffL; + ct = enc_one_round_6(ct, rkey); + } + return ct; +} + +/* Decrypt using the the Sodark-6 algorithm. + rounds Number of rounds. + ct Ciphertext (48 bits). + key Encryption key (56 bits). + tweak Tweak (64 bits). */ +static inline uint64_t decrypt_sodark_6(uint32_t rounds, uint64_t ct, uint64_t key, + uint64_t tweak) { + if (!g_sbox_dec_init) { + create_sodark_dec_sbox(); + } + uint32_t tshift = (48 * (rounds - 1)) % 64; + uint32_t kshift = (48 * (rounds - 1)) % 56; + tweak = (tweak >> (64 - tshift)) | (tweak << tshift); + key = ((key >> (56 - kshift)) | (key << kshift)) & 0xffffffffffffffL; + uint64_t pt = ct; + for (uint32_t round = 0; round < rounds; round++) { + uint64_t rkey = ((key >> 8) ^ (tweak >> 16)) & 0xffffffffffffL; + tweak = (tweak >> 48) | (tweak << 16); + key = ((key >> 48) | (key << 8)) & 0xffffffffffffffL; + pt = dec_one_round_6(pt, rkey); + } + return pt; +} + +#endif /* _SODARK_H_ */