diff --git a/tools/cryptorf/Makefile b/tools/cryptorf/Makefile new file mode 100644 index 000000000..3dd27d3ab --- /dev/null +++ b/tools/cryptorf/Makefile @@ -0,0 +1,30 @@ +CC = gcc +CXX = g++ +LD = gcc +LXX = g++ +CFLAGS = -W -Wall -O4 +CXXFLAGS = -W -Wall -O4 -std=c++11 +LDFLAGS = +LXXFLAGS = -lpthread + +OBJS = cryptolib.o util.o +HEADERS = cryptolib.h util.h +SRC = cryptolib.c util.c +EXES = cm sm sma sma_multi crf + +all: $(OBJS) $(EXES) + +%.o : %.c + $(CC) $(CFLAGS) -c -o $@ $< + +% : %.c $(OBJS) + $(LD) $(CFLAGS) $(LDFLAGS) -o $@ $< $(OBJS) + +% : %.cpp $(SRC) + $(LXX) $(CXXFLAGS) -o $@ $< $(SRC) $(LXXFLAGS) + +crf: crf.c $(OBJS) + $(LD) $(CFLAGS) $(LDFLAGS) -o crf $< $(OBJS) -lnfc + +clean: + rm -f $(OBJS) $(EXES) crf diff --git a/tools/cryptorf/cm.c b/tools/cryptorf/cm.c new file mode 100644 index 000000000..14e32844a --- /dev/null +++ b/tools/cryptorf/cm.c @@ -0,0 +1,89 @@ +/* + * + * CryptoMemory simulation + * + * Copyright (C) 2010, Flavio D. Garcia, Peter van Rossum, Roel Verdult + * and Ronny Wichers Schreur. Radboud University Nijmegen + * + * 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 "defines.h" +#include "cryptolib.h" +#include "util.h" + +int main(int argc, const char* argv[]) +{ + // Cryptomemory state + crypto_state_t s; + + // Main authentication values + byte_t Q[8]; // Reader key-auth random + byte_t Gc[8]; // Secret seed + byte_t Ci[8]; // Card random (last state) + byte_t Ch[8]; // Reader answer (challenge) + byte_t Ci_1[8]; // Card answer + byte_t Ci_2[8]; // Session key + + // Session authentication values + byte_t Qs[8]; // Reader session-auth random + byte_t Chs[8]; // Reader session-answer (challenge) + byte_t Ci_1s[8]; // Card answer for session + byte_t Ci_2s[8]; // Is this used? + + // Various argument options + ui64 nGc; // Card secret + ui64 nCi; // Card random + ui64 nQ; // Reader main-random + ui64 nQs; // Reader session-random + + // Show header and help syntax + printf("CryptoMemory simulator - (c) Radboud University Nijmegen\n"); + if (argc < 5) + { + printf("\nsyntax: cm \n"); + return 1; + } + + // Parse arguments + sscanf(argv[1],"%016llx",&nGc); num_to_bytes(nGc,8,Gc); + sscanf(argv[2],"%016llx",&nCi); num_to_bytes(nCi,8,Ci); + sscanf(argv[3],"%016llx",&nQ); num_to_bytes(nQ,8,Q); + sscanf(argv[4],"%016llx",&nQs); num_to_bytes(nQs,8,Qs); + + // Calculate authentication + cm_auth(Gc,Ci,Q,Ch,Ci_1,Ci_2,&s); + + printf("\nAuthenticate\n"); + printf(" Gc: "); print_bytes(Gc,8); + printf(" Ci: "); print_bytes(Ci,8); + printf(" Q: "); print_bytes(Q,8); + printf(" Ch: "); print_bytes(Ch,8); + printf(" Ci+1: "); print_bytes(Ci_1,8); + printf(" Ci+2: "); print_bytes(Ci_2,8); + + cm_auth(Ci_2,Ci_1,Qs,Chs,Ci_1s,Ci_2s,&s); + + printf("\nVerify Crypto (Session Key)\n"); + printf(" Gc(s): "); print_bytes(Ci_2,8); + printf(" Ci(s): "); print_bytes(Ci_1,8); + printf(" Q(s): "); print_bytes(Qs,8); + printf(" Ch(s): "); print_bytes(Chs,8); + printf("Ci+1(s): "); print_bytes(Ci_1s,8); + printf("Ci+2(s): "); print_bytes(Ci_2s,8); + + printf("\n"); + return 0; +} diff --git a/tools/cryptorf/crf.c b/tools/cryptorf/crf.c new file mode 100644 index 000000000..044acce05 --- /dev/null +++ b/tools/cryptorf/crf.c @@ -0,0 +1,294 @@ +/* + * + * CryptoRF simulation + * + * Copyright (C) 2010, Flavio D. Garcia, Peter van Rossum, Roel Verdult + * and Ronny Wichers Schreur. Radboud University Nijmegen + * + * 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 "defines.h" +#include +#include +#include +#include "cryptolib.h" +#include "util.h" + +// ~dirty globals for lazy use of libnfc +static dev_info* pdi; // NFC device info +static tag_info ti; // Tag info (card serial, etc.) +byte_t abtRx[MAX_FRAME_LEN]; // Communication buffer +size_t szRxLen; // Length of communication buffer + +void print_decryption(const byte_t* ct, const byte_t* pt, size_t len) { + size_t pos,count; + + for (count = 0; count < len; count += 8) { + printf(" "); + for (pos = 0; pos < 8; pos++){ + if ((count+pos) "); + for (pos = 0; pos < 8; pos++) { + if ((count + pos) < len) { + printf("%02x ", pt[count + pos]); + } else { + printf(" "); + } + } + printf("\n"); + } +} + +bool transmit_bytes(const byte_t* pbtTx, const size_t szTxLen) { + printf("R: "); + print_bytes(pbtTx, szTxLen); + + // Transmit the command bytes + if (!nfc_initiator_transceive_bytes(pdi, pbtTx, szTxLen, abtRx, (uint32_t*)&szRxLen)) { + printf("\nERROR: Communication failed\n\n"); + nfc_disconnect(pdi); + exit(1); + } + + printf("T: "); + print_bytes(abtRx, szRxLen); + + // Succesful transfer + return true; +} + + +#define PWD_NOT_USED (uint32_t)(~0) + +int main(int argc, char* argv[]) { + // Various parameters + crypto_state_t s; // Cryptomemory state + size_t pos; // Position counter + + // Main authentication values + byte_t Q[8]; // Reader key-auth random + byte_t Gc[8]; // Secret seed + byte_t Ci[8]; // Card random (last state) + byte_t Ch[8]; // Reader answer (challenge) + byte_t Ci_1[8]; // Card answer + byte_t Ci_2[8]; // Session key + + // Session authentication values + byte_t Qs[8]; // Reader session-auth random + byte_t Chs[8]; // Reader session-answer (challenge) + byte_t Ci_1s[8]; // Card answer for session + byte_t Ci_2s[8]; // Is this used? + + // Various argument options + ui64 Gc0; // First card secret + uint32_t zone; // Number of userzone + uint32_t offset; // Offset address + uint32_t len; // Length + uint32_t pwd; // Optional read password + + // Application buffers + byte_t pt[MAX_FRAME_LEN]; // Plaintext + byte_t ct[MAX_FRAME_LEN]; // Ciphertext + byte_t mac[2]; + + byte_t crf_read_ci[2 + 2] = { 0x16,0x00,0x50,0x07 }; // Read first card random Ci0 (offset 50, len 8) + byte_t crf_check_pwd[2 + 3] = { 0x1c,0x00 }; // Provide (optional) read password + byte_t crf_auth[2 + 16] = { 0x18,0x00 }; // Authenticate using card secret Gc0 and Ci + byte_t crf_verify[2 + 16] = { 0x18,0x10 }; // Authenticate with session key + byte_t crf_set_zone[1 + 1] = { 0x11 }; // Set the userzone to read from + byte_t crf_read_zone[2 + 2] = { 0x12,0x00 }; // Read n-bytes from offset + byte_t crf_read_mac[ 4] = { 0x16,0x02,0xff,0x01 }; // Read n-bytes from offset + + // Show header and help syntax + printf("CryptoRF example - (c) Radboud University Nijmegen\n\n"); + if (argc < 5) { + printf("syntax: crf [pwd]\n\n"); + return 1; + } + + // Parse command-line arguments + sscanf(argv[1],"%016llx", &Gc0); + sscanf(argv[2],"%02x", &zone); + sscanf(argv[3],"%02x", &offset); + sscanf(argv[4],"%02x", &len); + + // Construct CryptoRF frames + num_to_bytes(Gc0, 8, Gc); + crf_set_zone[1] = zone; + crf_read_zone[2] = offset; + crf_read_zone[3] = (len == 0) ? 0 : (len - 1); + + // Check if the optional password argument was used + if (argc == 6) { + sscanf(argv[5], "%06x", &pwd); + num_to_bytes(pwd, 3, crf_check_pwd + 2); + } else { + pwd = PWD_NOT_USED; + } + + // Initialize randoms + srand((uint32_t)time(0)); + + for (pos = 0; pos < 8; pos++) { + Q[pos] = rand(); + Qs[pos] = rand(); + } + + // Try to open the NFC device + pdi = nfc_connect(NULL); + if (pdi == INVALID_DEVICE_INFO) { + printf("ERROR: Unable to connect to NFC device.\n"); + return 1; + } + nfc_initiator_init(pdi); + + // Drop the field for a while + nfc_configure(pdi, DCO_ACTIVATE_FIELD, true); + + // Let the reader only try once to find a tag + nfc_configure(pdi, DCO_INFINITE_SELECT, false); + + // Configure the CRC and Parity settings + nfc_configure(pdi, DCO_HANDLE_CRC, true); + nfc_configure(pdi, DCO_HANDLE_PARITY, true); + + printf("Connected to NFC device: %s\n\n", pdi->acName); + + // Poll for a ISO14443-B cryptomemory tag + if (!nfc_initiator_select_tag(pdi, IM_ISO14443B_106, (byte_t*)"\x00", 1, &ti)) { + printf("ERROR: Can not find a Atmel CryptoRF card.\n\n"); + nfc_disconnect(pdi); + return 1; + } + + printf("The following (NFC) ISO14443-B tag was found:\n\n"); + printf(" ATQB: "); print_bytes(ti.tib.abtAtqb, 12); + printf(" ID: "); print_bytes(ti.tib.abtId, 4); + printf(" CID: %02x\n", ti.tib.btCid); + printf(" PARAMS: %02x %02x %02x %02x\n\n" + ,ti.tib.btParam1 + ,ti.tib.btParam2 + ,ti.tib.btParam3 + ,ti.tib.btParam4 + ); + + + printf("Changing active userzone\n"); + transmit_bytes(crf_set_zone, sizeof(crf_set_zone)); + printf("\n"); + + if (pwd != PWD_NOT_USED) { + printf("Suppling password for communication\n"); + transmit_bytes(crf_check_pwd, sizeof(crf_check_pwd)); + printf("\n"); + } + + printf("Reading first Ci(0) from the system zone (offset = 0x50)\n"); + transmit_bytes(crf_read_ci, sizeof(crf_read_ci)); + printf("\n"); + + // Save the retrieved value of Ci + memcpy(Ci, abtRx + 2, 8); + + // Calculate key-authentication + printf("* Computing authentication values with card secret\n\n"); + cm_auth(Gc, Ci, Q, Ch, Ci_1, Ci_2, &s); + memcpy(crf_auth + 2, Q, 8); + memcpy(crf_auth + 10, Ch, 8); + + printf("Authenticate using Gc, Ci and random Q\n"); + transmit_bytes(crf_auth, sizeof(crf_auth)); + printf("\n"); + + printf("Reading new Ci value from the system zone (tag-answer)\n"); + transmit_bytes(crf_read_ci, sizeof(crf_read_ci)); + printf("\n"); + + if (memcmp(Ci_1, abtRx + 2, 8) != 0) { + printf("ERROR: Authentication failed\n\n"); + nfc_disconnect(pdi); + return 1; + } + + // Calculate session-authentication + printf("* Computing authentication values with session key\n\n"); + cm_auth(Ci_2, Ci_1, Qs, Chs, Ci_1s, Ci_2s, &s); + memcpy(crf_verify + 2, Qs, 8); + memcpy(crf_verify + 10, Chs, 8); + + printf("VerifyCrypto using session key and initialize encryption\n"); + transmit_bytes(crf_verify, sizeof(crf_verify)); + printf("\n"); + + printf("Reading new Ci value from the system zone (tag-answer)\n"); + transmit_bytes(crf_read_ci, sizeof(crf_read_ci)); + printf("\n"); + + if (memcmp(Ci_1s, abtRx + 2, 8) != 0) { + printf("ERROR: Session authentication failed\n\n"); + nfc_disconnect(pdi); + return 1; + } + + printf("* Updating the cipher by grinding Ci (offset,len,data)\n\n"); + cm_grind_read_system_zone(0x50, 8, Ci_1s, &s); + + printf("Read the data from the offset using the encrypted channel\n"); + transmit_bytes(crf_read_zone, sizeof(crf_read_zone)); + printf("\n"); + + if (abtRx[1] != 0) { + printf("ERROR: Reading failed, maybe you need to supply a password\n\n"); + nfc_disconnect(pdi); + return 1; + } + memcpy(ct, abtRx + 2, len); + + printf("* Decrypting..."); + cm_decrypt(offset, len, ct, pt, &s); + printf("done\n\n"); + print_decryption(ct, pt, len); + printf("\n"); + + if (pwd != PWD_NOT_USED) { + num_to_bytes(pwd, 3, pt); + cm_password(pt, crf_check_pwd + 2, &s); + printf("Testing the feature to supply an encrypted password\n"); + transmit_bytes(crf_check_pwd, sizeof(crf_check_pwd)); + printf("\n"); + } + + // Calculate and check mac + cm_mac(mac, &s); + printf("Verify checksum for the transaction: %02x %02x\n", mac[0], mac[1]); + transmit_bytes(crf_read_mac, sizeof(crf_read_mac)); + if (memcmp(mac, abtRx + 2, 2) != 0) { + printf("ERROR: MAC checksum failed\n\n"); + nfc_disconnect(pdi); + return 1; + } + + printf("Communication succesful!\n\n"); + nfc_disconnect(pdi); + return 0; +} + diff --git a/tools/cryptorf/cryptolib.c b/tools/cryptorf/cryptolib.c new file mode 100644 index 000000000..7390f0438 --- /dev/null +++ b/tools/cryptorf/cryptolib.c @@ -0,0 +1,359 @@ +/* + * + * SecureMemory, CryptoMemory and CryptoRF library + * + * Copyright (C) 2010, Flavio D. Garcia, Peter van Rossum, Roel Verdult + * and Ronny Wichers Schreur. Radboud University Nijmegen + * + * 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 "cryptolib.h" + +typedef enum { + CA_ENCRYPT = 0x01, + CA_DECRYPT = 0x02 +} CryptoAction; + +int counter=0; + +byte_t nibbles_to_byte(nibble b0, nibble b1) +{ + // Combine both nibbles + return ((b0<<4)|b1); +} + +byte_t funny_mod(byte_t a, byte_t m) +{ + // Just return the input when this is less or equal than the modular value + if (a> (n_bits - 1))) & mask; +} + +void reconstruct_nibbles(crypto_state s) +{ + byte_t b1,b5,b8,b15,b18; + byte_t b0,b4,b7,b14,b17; + + // Extract the bytes that generated the "previous" nibble + b1 = (byte_t)((s->l >> 25) & 0x1f); + b5 = (byte_t)((s->l >> 5) & 0x1f); + b8 = (byte_t)((s->m >> 35) & 0x1f); + b15 = (byte_t)((s->r >> 15) & 0x1f); + b18 = (byte_t)(s->r & 0x1f); + + // Reconstruct the b0 nibble + s->b0 = ((b1 ^ b5) & 0x0f) & ~(b8); + s->b0 |= ((b15 ^ b18) & 0x0f) & b8; + + // Extract the bytes for the current nibble + b0 = (byte_t)((s->l >> 30) & 0x1f); + b4 = (byte_t)((s->l >> 10) & 0x1f); + b7 = (byte_t)((s->m >> 42) & 0x1f); + b14 = (byte_t)((s->r >> 20) & 0x1f); + b17 = (byte_t)((s->r >> 5) & 0x1f); + + // Construct the values for b1 generation + s->b1l = ((b0 ^ b4) & 0x0f); + s->b1r = ((b14 ^ b17) & 0x0f); + s->b1s = b7; + + // Reconstruct the b1 nibble + s->b1 = s->b1l & ~(s->b1s); + s->b1 |= s->b1r & s->b1s; +} + +void next_left(byte_t in, crypto_state s) +{ + byte_t b3, b6, bx; + + // Update the left cipher state with the input byte + s->l ^= ((in & 0x1f) << 20); + + // Extract the two (5 bits) values used for modular addtion + b3 = (byte_t)((s->l >> 15) & 0x1f); + b6 = (byte_t)(s->l & 0x1f); + + // Compute the modular addition + bx = funny_mod(b3 + bit_rotate_left(b6,5),0x1f); + + // Rotate the left cipher state 5 bits + s->l = ((s->l >> 5)| ((uint64_t)bx << 30)); + + // Save the 4 left output bits used for b1 + s->b1l = ((bx^b3) & 0x0f); +} + +void next_right(byte_t in, crypto_state s) +{ + byte_t b16, b18, bx; + + // Update the right cipher state with the input byte + s->r ^= ((in & 0xf8) << 12); + + // Extract the two (5 bits) values used for modular addtion + b16 = (byte_t)((s->r >> 10) & 0x1f); + b18 = (byte_t)(s->r & 0x1f); + + // Compute the modular addition + bx = funny_mod(b18 + b16,0x1f); + + // Rotate the right cipher state 5 bits + s->r = ((s->r >> 5) | ((uint64_t)bx << 20)); + + // Save the 4 right output bits used for b1 + s->b1r = ((bx^b16) & 0x0f); +} + +void next_middle(byte_t in, crypto_state s) +{ + byte_t b12, b13, bx; + + // Update the middle cipher state with the input byte + s->m ^= (((((uint64_t)in << 3) & 0x7f) | (in >> 5)) << 14); + + // Extract the two (7 bits) values used for modular addtion + b12 = (byte_t)((s->m >> 7) & 0x7f); + b13 = (byte_t)(s->m & 0x7f); + + // Compute the modular addition + bx = (funny_mod(b12 + bit_rotate_left(b13,7),0x7f)); + + // Rotate the middle cipher state 7 bits + s->m = ((s->m >> 7)| ((uint64_t)bx << 42)); + + // Save the 4 middle selector bits used for b1 + s->b1s = bx & 0x0f; +} + +void next(const bool feedback, byte_t in, crypto_state s) +{ + // Initialize the (optional) input parameter + byte_t a = in; + + // Only Cryptomemory uses feedback + if (feedback) + { + // Construct the cipher update 'a' from (input ^ feedback) + a = in ^ nibbles_to_byte(s->b0,s->b1); + } + + // Shift the cipher state + next_left(a,s); + next_middle(a,s); + next_right(a,s); + + // For active states we can use the available (previous) 'b1' nibble, + // otherwise use reconstruct_nibbles() to generate them + // reconstruct_nibbles(s) + + // The nible from b1 shifts to b0 + s->b0 = s->b1; + + // Construct the new value of nible b1 + s->b1 = s->b1l & ~(s->b1s); + s->b1 |= s->b1r & s->b1s; +} + +void next_n(const bool feedback, size_t n, byte_t in, crypto_state s) +{ + // While n-rounds left, shift the cipher + while (n--) next(feedback,in, s); +} + +void initialize(const bool feedback, const byte_t* Gc, const byte_t* Ci, const byte_t* Q, const size_t n, crypto_state s) +{ + size_t pos; + + // Reset the cipher state + memset(s,0x00,sizeof(crypto_state_t)); + + // Load in the ci (tag-nonce), together with the first half of Q (reader-nonce) + for (pos = 0; pos < 4; pos++) + { + next_n(feedback,n,Ci[2*pos ],s); + next_n(feedback,n,Ci[2*pos+1],s); + next(feedback,Q[pos],s); + } + + // Load in the diversified key (Gc), together with the second half of Q (reader-nonce) + for (pos = 0; pos < 4; pos++) + { + next_n(feedback,n,Gc[2*pos ],s); + next_n(feedback,n,Gc[2*pos+1],s); + next(feedback,Q[pos+4],s); + } +} + +byte_t cm_byte(crypto_state s) +{ + // Construct keystream byte by combining both nibbles + return nibbles_to_byte(s->b0,s->b1); +} + +byte_t sm_byte(crypto_state s) { + byte_t ks; + + // Construct keystream byte by combining 2 parts from 4 nibbles + next_n(false,2,0,s); + ks = s->b1 << 4; + next_n(false,2,0,s); + ks |= s->b1; + + return ks; +} + +void print_crypto_state(const char* text,crypto_state s) { + int pos; + + printf("%s",text); + for(pos = 6; pos >= 0; pos--) + printf(" %02x", (byte_t)(s->l >> (pos * 5)) & 0x1f); + + printf(" |"); + for(pos = 6; pos >= 0; pos--) + printf(" %02x", (byte_t)(s->m >> (pos * 7)) & 0x7f); + + printf(" |"); + for(pos = 4; pos >= 0; pos--) + printf(" %02x", (byte_t)(s->r >> (pos * 5)) & 0x1f); + + printf(" | %02x",cm_byte(s)); + printf("\n"); +} + +void sm_auth(const byte_t* Gc, const byte_t* Ci, const byte_t* Q, byte_t* Ch, byte_t* Ci_1, crypto_state s) { + size_t pos; + + initialize(false,Gc,Ci,Q,1,s); + + // Generate challange answer for Tag and Reader + for (pos=0; pos<8; pos++) { + Ci_1[pos] = sm_byte(s); + Ch[pos] = sm_byte(s); + } +} + +void cm_auth(const byte_t* Gc, const byte_t* Ci, const byte_t* Q, byte_t* Ch, byte_t* Ci_1, byte_t* Ci_2, crypto_state s) { + size_t pos; + + initialize(true,Gc,Ci,Q,3,s); + + // Construct the reader-answer (challange) + next_n(true,6,0,s); + Ch[0] = cm_byte(s); + for (pos = 1; pos < 8; pos++) + { + next_n(true,7,0,s); + Ch [pos] = cm_byte(s); + } + + // Construct the tag-answer (Ci+1 = ff .. .. .. .. .. .. ..) + Ci_1[0] = 0xff; + for (pos = 1; pos < 8; pos++) + { + next_n(true,2,0,s); + Ci_1[pos] = cm_byte(s); + } + + // Construct the session key (Ci+2) + for (pos = 0; pos < 8; pos++) + { + next_n(true,2,0,s); + Ci_2[pos] = cm_byte(s); + } + + // Prepare the cipher for encryption by shifting 3 more times + next_n(true,3,0,s); +} + +void cm_crypt(const CryptoAction ca, const byte_t offset, const byte_t len, const byte_t* in, byte_t* out, crypto_state s) { + size_t pos; + byte_t bt; + + next_n(true,5,0,s); + next(true,offset,s); + next_n(true,5,0,s); + next(true,len,s); + for (pos=0; pos. + * + */ + +#ifndef _CRYPTOLIB_H_ +#define _CRYPTOLIB_H_ + +#include "defines.h" +#include +#include +#include + +// A nibble is actually only 4 bits, but there is no such type ;) +typedef byte_t nibble; + +typedef struct { + uint64_t l; + uint64_t m; + uint64_t r; + nibble b0; + nibble b1; + nibble b1l; + nibble b1r; + nibble b1s; +}crypto_state_t; +typedef crypto_state_t* crypto_state; + +void print_crypto_state(const char* text,crypto_state s); +void sm_auth(const byte_t* Gc, const byte_t* Ci, const byte_t* Q, byte_t* Ch, byte_t* Ci_1, crypto_state s); +void cm_auth(const byte_t* Gc, const byte_t* Ci, const byte_t* Q, byte_t* Ch, byte_t* Ci_1, byte_t* Ci_2, crypto_state s); +void cm_encrypt(const byte_t offset, const byte_t len, const byte_t* pt, byte_t* ct, crypto_state s); +void cm_decrypt(const byte_t offset, const byte_t len, const byte_t* ct, byte_t* pt, crypto_state s); +void cm_grind_read_system_zone(const byte_t offset, const byte_t len, const byte_t* pt, crypto_state s); +void cm_grind_set_user_zone(const byte_t zone, crypto_state s); +void cm_mac(byte_t* mac, crypto_state s); +void cm_password(const byte_t* pt, byte_t* ct, crypto_state s); + +#endif // _CRYPTOLIB_H_ diff --git a/tools/cryptorf/defines.h b/tools/cryptorf/defines.h new file mode 100644 index 000000000..4962d7981 --- /dev/null +++ b/tools/cryptorf/defines.h @@ -0,0 +1,36 @@ +/* + * + * Defines + * + * Copyright (C) 2010, Flavio D. Garcia, Peter van Rossum, Roel Verdult + * and Ronny Wichers Schreur. Radboud University Nijmegen + * + * 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 _DEFINES_H_ +#define _DEFINES_H_ + +#define _CRT_SECURE_NO_WARNINGS +#define _CRT_SECURE_NO_DEPRECATE +#include +#include +#include +#include +#define null 0 +typedef unsigned char byte_t; +typedef long long unsigned int ui64; + +#endif // _DEFINES_H_ diff --git a/tools/cryptorf/laundry.sh b/tools/cryptorf/laundry.sh new file mode 100755 index 000000000..46fd94bfe --- /dev/null +++ b/tools/cryptorf/laundry.sh @@ -0,0 +1,2 @@ +#!/bin/sh +./crf 4f794a463ff81d81 2 0 80 546dae diff --git a/tools/cryptorf/sm.c b/tools/cryptorf/sm.c new file mode 100644 index 000000000..c432440e6 --- /dev/null +++ b/tools/cryptorf/sm.c @@ -0,0 +1,77 @@ +/* + * + * SecureMemory simulation + * + * Copyright (C) 2010, Flavio D. Garcia, Peter van Rossum, Roel Verdult + * and Ronny Wichers Schreur. Radboud University Nijmegen + * + * 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 "defines.h" +#include "cryptolib.h" +#include "util.h" +#include + +int main(int argc, const char* argv[]) +{ + // Cryptomemory state + crypto_state_t s; + size_t pos; + + byte_t Q[8] = {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; // Reader random + byte_t Gc[8] = {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; // Secret seed + byte_t Ci[8] = {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; // Card random (last state) + byte_t Ch[8] = {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; // Reader answer + byte_t Ci_1[8] = {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; // Card answer + + // Various argument options + ui64 nGc; // Card secret + ui64 nCi; // Card random + ui64 nQ; // Reader main-random + + // Show header and help syntax + printf("SecureMemory simulator - (c) Radboud University Nijmegen\n"); + if (argc < 4) + { + printf("\nsyntax: sm \n"); + return 1; + } + + // Parse arguments + sscanf(argv[1],"%016llx",&nGc); num_to_bytes(nGc,8,Gc); + sscanf(argv[2],"%016llx",&nCi); num_to_bytes(nCi,8,Ci); + sscanf(argv[3],"%016llx",&nQ); num_to_bytes(nQ,8,Q); + + // Calculate authentication + sm_auth(Gc,Ci,Q,Ch,Ci_1,&s); + + printf("\nAuthentication info\n\n"); + printf(" Gc: "); print_bytes(Gc,8); + printf(" Ci: "); print_bytes(Ci,8); + printf(" Q: "); print_bytes(Q,8); + printf(" Ch: "); print_bytes(Ch,8); + printf("Ci+1: "); print_bytes(Ci_1,8); + printf("\n"); + printf(" Ks: "); + for (pos=0; pos<8; pos++) + { + printf("%02x ",Ci_1[pos]); + printf("%02x ",Ch[pos]); + } + printf("\n\n"); + + return 0; +} diff --git a/tools/cryptorf/sma.cpp b/tools/cryptorf/sma.cpp new file mode 100644 index 000000000..e8f1145fd --- /dev/null +++ b/tools/cryptorf/sma.cpp @@ -0,0 +1,918 @@ +/* + * + * SecureMemory recovery + * + * Copyright (C) 2010, Flavio D. Garcia, Peter van Rossum, Roel Verdult + * and Ronny Wichers Schreur. Radboud University Nijmegen + * + * 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 . + * + * Modified Iceman, 2020 + */ + +#include "defines.h" +#include "cryptolib.h" +#include "util.h" +#include +#include +#include +#include +#include +#include // sort, max_element, random_shuffle, remove_if, lower_bound +#include // greater, bind2nd + +using namespace std; + +#ifdef _MSC_VER + #define inline __inline +#endif + +/* +>./sm 4f794a463ff81d81 ffffffffffffffff 1234567812345678 +SecureMemory simulator - (c) Radboud University Nijmegen + +Authenticate + Gc: 4f 79 4a 46 3f f8 1d 81 + Ci: ff ff ff ff ff ff ff ff + Q: 12 34 56 78 12 34 56 78 + Ch: 88 c9 d4 46 6a 50 1a 87 +Ci+1: de c2 ee 1b 1c 92 76 e9 + + Ks: de 88 c2 c9 ee d4 1b 46 1c 6a 92 50 76 1a e9 87 + + left: 1ddeac626 + right: 19aba45 + + left-candidates bins: + 004df8a64 (74) + 0059ff7d5 (81) + 00d2ff4ed (80) + 032df8b12 (78) + 0337b8b7d (87) + 036f7b607 (77) + 03a6f882a (79) + 03b2ff59b (76) + 04445c715 (74) + 0452175be (80) + 0b29f2a5b (78) + 0f6c834fb (76) + 0f78aac5b (75) + 0f79c8d49 (78) + 109691f61 (70) + 159d1687e (86) + 176e73456 (77) + 1ddeac626 (92) + 1facee6e5 (78) + 2049ed469 (80) + 205078bba (74) + 31c277406 (81) + 31c2777e6 (81) + 3770cdaf3 (74) + 48916e84e (77) + 4ba9b6520 (78) + 4ba9b653f (78) + 4c51c6463 (82) + 4c9432733 (76) + 4e3d88819 (81) + 4e3d88bf9 (81) + 51c8755b5 (76) + 5b2aeb858 (76) + 5fb612b96 (80) + 60531191a (78) + 6221539d9 (92) + 68918cba9 (79) + 6c9a11672 (78) + 6f696e09e (70) + 7086372b6 (78) + 7bade8a41 (82) + 7c90849f8 (77) + 7cc847482 (87) + +*/ + +const uint64_t left_candidates[43] = { + 0x6221539d9ull, 0x1ddeac626ull, 0x7cc847482ull, 0x0337b8b7dull, + 0x159d1687eull, 0x7bade8a41ull, 0x4c51c6463ull, 0x4e3d88bf9ull, + 0x4e3d88819ull, 0x31c2777e6ull, 0x31c277406ull, 0x0059ff7d5ull, + 0x5fb612b96ull, 0x2049ed469ull, 0x0452175beull, 0x00d2ff4edull, + 0x68918cba9ull, 0x03a6f882aull, 0x7086372b6ull, 0x6c9a11672ull, + 0x60531191aull, 0x4ba9b653full, 0x4ba9b6520ull, 0x1facee6e5ull, + 0x0f79c8d49ull, 0x0b29f2a5bull, 0x032df8b12ull, 0x7c90849f8ull, + 0x48916e84eull, 0x176e73456ull, 0x036f7b607ull, 0x5b2aeb858ull, + 0x51c8755b5ull, 0x4c9432733ull, 0x0f6c834fbull, 0x03b2ff59bull, + 0x0f78aac5bull, 0x3770cdaf3ull, 0x205078bbaull, 0x04445c715ull, + 0x004df8a64ull, 0x6f696e09eull, 0x109691f61ull +}; + +typedef struct { + uint64_t l; + uint64_t m; + uint64_t r; + nibble b0; + nibble b1; + nibble b1l; + nibble b1r; + nibble b1s; + bool invalid; + byte_t Gc[8]; +}cs_t; +typedef cs_t* pcs; + +typedef struct { + byte_t addition; + byte_t out; +} lookup_entry; + +enum cipher_state_side { + CSS_LEFT, + CSS_RIGHT +}; + +void print_cs(const char* text,pcs s) +{ + int pos; + + printf("%s",text); + for(pos=6;pos>=0;pos--) + printf(" %02x",(byte_t)(s->l>>(pos*5))&0x1f); + printf(" |"); + for(pos=6;pos>=0;pos--) + printf(" %02x",(byte_t)(s->m>>(pos*7))&0x7f); + printf(" |"); + for(pos=4;pos>=0;pos--) + printf(" %02x",(byte_t)(s->r>>(pos*5))&0x1f); + + printf("\n"); +} + +static inline byte_t mod(byte_t a, byte_t m) +{ + // Just return the input when this is less or equal than the modular value + if (a> (n_bits - 1))) & mask; +} + +static inline byte_t bit_rotate_r(byte_t a, byte_t n_bits) +{ + return ((a >> 1) | ((a&1) << (n_bits - 1))); +} + +static byte_t lookup_left_substraction[0x400]; +static byte_t lookup_right_subtraction[0x400]; +static lookup_entry lookup_left[0x100000]; +static lookup_entry lookup_right[0x8000]; +static byte_t left_addition[0x100000]; + +static inline void init_lookup_left() +{ + byte_t b3,b6,temp; + int i,index; + + for(i = 0; i <0x400; i++) + { + b6 = i & 0x1f; + b3 = (i >> 5) & 0x1f; + index = (b3 << 15) | b6; + b6 = bit_rotate_l(b6, 5); + + temp = mod(b3 + b6,0x1f); + left_addition[index] = temp; + lookup_left[index].addition = temp; + lookup_left[index].out = ((temp^b3) & 0x0f); + } +} + +static inline void init_lookup_right() +{ + byte_t b16,b18,temp; + int i,index; + + for(i = 0; i <0x400; i++) + { + b18 = i & 0x1f; + b16 = (i >> 5) & 0x1f; + index = (b16 << 10) | b18; + + temp = mod(b18 + b16,0x1f); + lookup_right[index].addition = temp; + lookup_right[index].out = ((temp^b16) & 0x0f); + } +} + +static void init_lookup_left_substraction() +{ + for(int index = 0; index < 0x400 ; index++) + { + byte_t b3 = (index >> 5 & 0x1f); + byte_t bx = (index & 0x1f); + lookup_left_substraction[index] = bit_rotate_r(mod((bx+0x1f)-b3,0x1f),5); + } +} + +static void init_lookup_right_substraction() +{ + for(int index = 0; index < 0x400 ; index++) + { + int b16 = (index >>5); + byte_t bx = (index & 0x1f); + lookup_right_subtraction[index] = mod((bx+0x1f)-b16,0x1f); + } +} + +static inline void previous_left(byte_t in, vector *candidate_states) +{ + pcs state; + size_t size = candidate_states->size(); + for(size_t pos=0; posl >> 30) & 0x1f); + unsigned b3 = (unsigned)(state->l >> 5) & 0x3e0; + state->l = (state->l << 5); + + //Ignore impossible states + if (bx == 0) + { + // Are we dealing with an impossible state? + if (b3 != 0) + { + state->invalid = true; + } else { + // We only need to consider b6=0 + state->l &= 0x7ffffffe0ull; + state->l ^= (((uint64_t)in & 0x1f) << 20); + } + } else { + byte_t b6 = lookup_left_substraction[b3|bx]; + state->l = (state->l & 0x7ffffffe0ull) | b6; + state->l ^= (((uint64_t)in & 0x1f) << 20); + + // Check if we have a second candidate + if (b6 == 0x1f) + { + cs_t nstate = *state; + nstate.l &= 0x7ffffffe0ull; + candidate_states->push_back(nstate); + } + } + } +} + +static inline void previous_right(byte_t in, vector *candidate_states) +{ + pcs state; + size_t size = candidate_states->size(); + for(size_t pos=0; posr >> 20) & 0x1f); + unsigned b16 = (unsigned)(state->r & 0x3e0);//(state->buffer_r >> 10) & 0x1f; + + state->r = (state->r << 5); + + // Ignore impossible states + if (bx == 0) + { + if (b16 != 0) + { + state->invalid=true; + } else { + // We only need to consider b18=0 + state->r &= 0x1ffffe0ull; + state->r ^= (((uint64_t)in & 0xf8) << 12); + } + } else{ + byte_t b18 = lookup_right_subtraction[b16|bx]; + state->r = (state->r & 0x1ffffe0ull) | b18; + state->r ^= (((uint64_t)in & 0xf8) << 12); + //state->b_right = ((b14^b17) & 0x0f); + + // Check if we have a second candidate + if (b18 == 0x1f) + { + cs_t nstate = *state; + nstate.r &= 0x1ffffe0ull; + candidate_states->push_back(nstate); + } + } + } +} + +static inline byte_t next_left_fast(byte_t in, uint64_t* left) +{ + if (in) *left ^= ((in & 0x1f) << 20); + lookup_entry* lookup = &(lookup_left[((*left) & 0xf801f)]); + *left = (((*left) >> 5)| ((uint64_t)lookup->addition << 30)); + return lookup->out; +} + +static inline byte_t next_left_ksbyte(uint64_t* left) +{ + lookup_entry* lookup; + byte_t bt; + + *left = (((*left) >> 5)| ((uint64_t)left_addition[((*left) & 0xf801f)] << 30)); + lookup = &(lookup_left[((*left) & 0xf801f)]); + *left = (((*left) >> 5)| ((uint64_t)lookup->addition << 30)); + bt = lookup->out << 4; + *left = (((*left) >> 5)| ((uint64_t)left_addition[((*left) & 0xf801f)] << 30)); + lookup = &(lookup_left[((*left) & 0xf801f)]); + *left = (((*left) >> 5)| ((uint64_t)lookup->addition << 30)); + bt |= lookup->out; + return bt; +} + +static inline byte_t next_right_fast(byte_t in, uint64_t* right) +{ + if (in) *right ^= ((in&0xf8) << 12); + lookup_entry* lookup = &(lookup_right[((*right) & 0x7c1f)]); + *right = (((*right) >> 5) | (lookup->addition << 20)); + return lookup->out; +} + +static inline void sm_left_mask(const byte_t* ks, byte_t* mask, uint64_t rstate) +{ + size_t pos; + byte_t bt; + + for (pos=0; pos<16; pos++) + { + next_right_fast(0,&rstate); + bt = next_right_fast(0,&rstate) << 4; + next_right_fast(0,&rstate); + bt |= next_right_fast(0,&rstate); + + // xor the bits with the keystream and count the "correct" bits + bt ^= ks[pos]; + + // Save the mask for the left produced bits + mask[pos] = bt; + } +} + +static inline uint32_t sm_right(const byte_t* ks, byte_t* mask, vector* pcrstates) +{ + byte_t tmp_mask[16]; + size_t pos,bits,bit,topbits; + uint64_t rstate,counter; + map bincstates; + map::iterator it; + byte_t bt; + + topbits = 0; + for (counter=0; counter<0x2000000; counter++) + { + // Reset the current bitcount of correct bits + bits = 0; + + // Copy the state we are going to test + rstate = counter; + + for (pos=0; pos<16; pos++) + { + next_right_fast(0,&rstate); + bt = next_right_fast(0,&rstate) << 4; + next_right_fast(0,&rstate); + bt |= next_right_fast(0,&rstate); + + // xor the bits with the keystream and count the "correct" bits + bt ^= ks[pos]; + + // Save the mask for the left produced bits + tmp_mask[pos] = bt; + + for (bit=0; bit<8; bit++) + { + // When the bit is xored away (=zero), it was the same, so correct ;) + if ((bt & 0x01) == 0) bits++; + bt >>= 1; + } + } + + if (bits > topbits) + { + topbits = bits; + // Copy the winning mask + memcpy(mask,tmp_mask,16); + } + + // Ignore states under 90 + if (bits >= 90) + { + // Make sure the bits are used for ordering + bincstates[(((uint64_t)bits)<<56) | counter] = counter; + } + + if ((counter&0xfffff) == 0) + { + printf("."); + fflush(stdout); + } + } + printf("\n"); + + // Clear the candidate state vector + pcrstates->clear(); + + // Copy the order the states from lowest-bin to highest-bin + for(it=bincstates.begin();it!=bincstates.end();++it) + { + pcrstates->push_back(it->second); + } + // Reverse the vector order (so the higest bin comes first) + reverse(pcrstates->begin(),pcrstates->end()); + + return topbits; +} + +static inline void previous_all_input(vector *pcstates, uint32_t gc_byte_index, cipher_state_side css) +{ + byte_t btGc,in; + vector ncstates; + vector prev_ncstates; + vector::iterator it,itnew; + + // Loop through the complete entryphy of 5 bits for each candidate + // We ignore zero (xor 0x00) to avoid duplicates + for (btGc=0; btGc<0x20; btGc++) + { + // Copy the original candidates that are supplied + ncstates = *pcstates; + + // Rollback the (candidate) cipher states with this input + if (css == CSS_RIGHT) + { + in = btGc << 3; + previous_right(in,&ncstates); + } else { + in = btGc; + previous_left(in,&ncstates); + } + + for(itnew=ncstates.begin();itnew!=ncstates.end();++itnew) + { + // Wipe away the invalid states + if (itnew->invalid == false) + { + itnew->Gc[gc_byte_index] = in; + prev_ncstates.push_back(*itnew); + } + } + } + + // Copy the previous states into the vector + *pcstates = prev_ncstates; +} + +static inline void search_gc_candidates_right(const uint64_t rstate_before_gc, const uint64_t rstate_after_gc, const byte_t* Q, vector* pcstates) +{ + vector::iterator it; + vector csl_cand; + map matchbox; + map::iterator itmatch; + uint64_t rstate; + size_t counter; + cs_t state; + + // Generate 2^20 different (5 bits) values for the first 4 Gc bytes (0,1,2,3) + for (counter=0; counter<0x100000; counter++) + { + rstate = rstate_before_gc; + next_right_fast((counter >> 12) & 0xf8,&rstate); + next_right_fast((counter >> 7) & 0xf8,&rstate); + next_right_fast(Q[4],&rstate); + next_right_fast((counter >> 2) & 0xf8,&rstate); + next_right_fast((counter << 3) & 0xf8,&rstate); + next_right_fast(Q[5],&rstate); + matchbox[rstate] = counter; + } + + // Reset and initialize the cryptostate and vecctor + memset(&state,0x00,sizeof(cs_t)); + state.invalid = false; + state.r = rstate_after_gc; + csl_cand.clear(); + csl_cand.push_back(state); + + // Generate 2^20(+splitting) different (5 bits) values for the last 4 Gc bytes (4,5,6,7) + previous_right(Q[7],&csl_cand); + previous_all_input(&csl_cand,7,CSS_RIGHT); + previous_all_input(&csl_cand,6,CSS_RIGHT); + previous_right(Q[6],&csl_cand); + previous_all_input(&csl_cand,5,CSS_RIGHT); + previous_all_input(&csl_cand,4,CSS_RIGHT); + + pcstates->clear(); + + // Take the intersection of the corresponding states ~2^15 values (40-25 = 15 bits) + for(it=csl_cand.begin();it!=csl_cand.end();++it) + { + itmatch = matchbox.find(it->r); + if (itmatch != matchbox.end()) + { + it->Gc[0] = (itmatch->second >> 12) & 0xf8; + it->Gc[1] = (itmatch->second >> 7) & 0xf8; + it->Gc[2] = (itmatch->second >> 2) & 0xf8; + it->Gc[3] = (itmatch->second << 3) & 0xf8; + /* + printf("%07llx ",it->r); + printf("(%x)\n",itmatch->second); + */ +/* + if (it->r == 0xff459b) + { + print_cs("previous:",&(*it)); + printf("%07llx\n",it->r); + print_bytes(it->Gc,8); + } +*/ + pcstates->push_back(*it); + } + } +} + +static inline void sm_left(const byte_t* ks, byte_t* mask, vector* pcstates) +{ + map bincstates; + map::iterator it; + uint64_t counter,lstate; + size_t pos,bits,bit; + byte_t correct_bits[16]; + byte_t bt; + cs_t state; + lookup_entry* lookup; + + // Reset and initialize the cryptostate and vecctor + memset(&state,0x00,sizeof(cs_t)); + state.invalid = false; + + for (counter=0; counter<0x800000000ull; counter++) + { + lstate = counter; + + for (pos=0; pos<16; pos++) + { + lstate = (((lstate) >> 5)| ((uint64_t)left_addition[((lstate) & 0xf801f)] << 30)); + lookup = &(lookup_left[((lstate) & 0xf801f)]); + lstate = (((lstate) >> 5)| ((uint64_t)lookup->addition << 30)); + bt = lookup->out << 4; + lstate = (((lstate) >> 5)| ((uint64_t)left_addition[((lstate) & 0xf801f)] << 30)); + lookup = &(lookup_left[((lstate) & 0xf801f)]); + lstate = (((lstate) >> 5)| ((uint64_t)lookup->addition << 30)); + bt |= lookup->out; + + // xor the bits with the keystream and count the "correct" bits + bt ^= ks[pos]; + + // When the REQUIRED bits are NOT xored away (=zero), ignore this wrong state + if ((bt & mask[pos]) != 0) break; + + // Save the correct bits for statistical information + correct_bits[pos] = bt; + } + + // If we have parsed all 16 bytes of keystream, we have a valid CANDIDATE! + if (pos == 16) + { + // Count the total correct bits + bits=0; + for (pos=0; pos<16; pos++) + { + // Get the next byte-value with correct bits + bt = correct_bits[pos]; + + // Count all the (correct) bits + for (bit=0; bit<8; bit++) + { + // When the bit is xored away (=zero), it was the same, so correct ;) + if ((bt & 0x01) == 0) bits++; + bt >>= 1; + } + } + + // Print the left candidate +// printf("%09llx (%d)\n",counter,bits); + printf("."); + fflush(stdout); + + state.l = counter; + // Make sure the bits are used for ordering + bincstates[(((uint64_t)bits)<<56) | counter] = state; + } + + if ((counter&0xffffffffull) == 0) + { + printf("%02.1f%%.",((float)100/8)*(counter>>32)); + fflush(stdout); + } + } + printf("100%%\n"); + + // Clear the candidate state vector + pcstates->clear(); + + // Copy the order the states from lowest-bin to highest-bin + for(it=bincstates.begin();it!=bincstates.end();++it) + { + pcstates->push_back(it->second); + } + // Reverse the vector order (so the higest bin comes first) + reverse(pcstates->begin(),pcstates->end()); +} + +static inline void search_gc_candidates_left(const uint64_t lstate_before_gc, const byte_t* Q, vector* pcstates) +{ + vector csl_cand,csl_search; + vector::iterator itsearch,itcand; + map matchbox; + map::iterator itmatch; + uint64_t lstate; + size_t counter; + + // Generate 2^20 different (5 bits) values for the first 4 Gc bytes (0,1,2,3) + for (counter=0; counter<0x100000; counter++) + { + lstate = lstate_before_gc; + next_left_fast((counter >> 15) & 0x1f,&lstate); + next_left_fast((counter >> 10) & 0x1f,&lstate); + next_left_fast(Q[4],&lstate); + next_left_fast((counter >> 5) & 0x1f,&lstate); + next_left_fast(counter & 0x1f,&lstate); + next_left_fast(Q[5],&lstate); + matchbox[lstate] = counter; + } + + // Copy the input candidate states and clean the output vector + csl_cand = *pcstates; + pcstates->clear(); + + for(itcand=csl_cand.begin();itcand!=csl_cand.end();++itcand) + { + csl_search.clear(); + csl_search.push_back(*itcand); + + // Generate 2^20(+splitting) different (5 bits) values for the last 4 Gc bytes (4,5,6,7) + previous_left(Q[7],&csl_search); + previous_all_input(&csl_search,7,CSS_LEFT); + previous_all_input(&csl_search,6,CSS_LEFT); + previous_left(Q[6],&csl_search); + previous_all_input(&csl_search,5,CSS_LEFT); + previous_all_input(&csl_search,4,CSS_LEFT); + + // Take the intersection of the corresponding states ~2^15 values (40-25 = 15 bits) + for(itsearch=csl_search.begin();itsearch!=csl_search.end();++itsearch) + { + itmatch = matchbox.find(itsearch->l); + if (itmatch != matchbox.end()) + { + itsearch->Gc[0] = (itmatch->second >> 15) & 0x1f; + itsearch->Gc[1] = (itmatch->second >> 10) & 0x1f; + itsearch->Gc[2] = (itmatch->second >> 5) & 0x1f; + itsearch->Gc[3] = itmatch->second & 0x1f; + + /* + printf("%07llx ",it->l); + printf("(%x) ",itmatch->second); + print_cs("",&(*it)); + */ +/* + if (itsearch->l == 0x405162420ull) + { + print_cs("previous:",&(*itsearch)); + printf("%09llx\n",itsearch->l); + print_bytes(itsearch->Gc,8); + } + count++; +*/ + pcstates->push_back(*itsearch); + } + } +// printf("%09llx: ",itcand->l); +// printf("%d - %d\n",csl_search.size(),count); + printf("."); + fflush(stdout); + } + printf("\n"); +} + +void combine_valid_left_right_states(vector* plcstates, vector* prcstates, vector* pgc_candidates) +{ + vector::iterator itl, itr; + size_t pos,count; + uint64_t gc; + bool valid; + + // Clean up the candidate list + pgc_candidates->clear(); + count = 0; + for(itl=plcstates->begin();itl!=plcstates->end();++itl) + { + for(itr=prcstates->begin();itr!=prcstates->end();++itr) + { + valid = true; + // Check for left and right candidates that share the overlapping bits (8 x 2bits of Gc) + for (pos=0; pos<8; pos++) + { + if ((itl->Gc[pos] & 0x18) != (itr->Gc[pos] & 0x18)) + { + valid = false; + break; + } + } + + if (valid) + { + gc = 0; + for (pos=0; pos<8; pos++) + { + gc <<= 8; + gc |= (itl->Gc[pos] | itr->Gc[pos]); + } +// printf("%016llx\n",gc); + pgc_candidates->push_back(gc); +// printf("%09llx - ",itl->l); +// printf("%07llx\n",itr->r); + } + count++; + } + } + printf("Found a total of " _YELLOW_("%llu")" combinations, ",((unsigned long long)plcstates->size()) * prcstates->size()); + printf("but only " _GREEN_("%lu")" were valid!\n", pgc_candidates->size()); +} + +int main(int argc, const char* argv[]) +{ + size_t pos; + crypto_state_t ostate; + uint64_t rstate_before_gc,rstate_after_gc; + uint64_t lstate_before_gc; + vector rstates,lstates_after_gc,pgc_candidates; + vector::iterator itrstates,itgc; + vector crstates; + vector clcandidates,clstates; + vector::iterator it; + uint32_t rbits; + +// byte_t Gc[ 8] = {0x4f,0x79,0x4a,0x46,0x3f,0xf8,0x1d,0x81}; +// byte_t Gc[ 8] = {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; +// byte_t Ci[ 8] = {0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff}; +// byte_t Q[ 8] = {0x12,0x34,0x56,0x78,0x12,0x34,0x56,0x78}; + byte_t Gc[ 8]; + byte_t Ci[ 8]; + byte_t Q[ 8]; + byte_t Ch[ 8]; + byte_t Ci_1[ 8]; + + byte_t Gc_chk[ 8]; + byte_t Ch_chk[ 8]; + byte_t Ci_1_chk[ 8]; + + // byte_t ks[16] = {0xde,0x88,0xc2,0xc9,0xee,0xd4,0x1b,0x46,0x1c,0x6a,0x92,0x50,0x76,0x1a,0xe9,0x87}; + // byte_t mask[16] = {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; + // byte_t mask[16] = {0x04,0xb0,0xe1,0x10,0xc0,0x33,0x44,0x20,0x20,0x00,0x70,0x8c,0x22,0x04,0x10,0x80}; + + byte_t ks[16]; + byte_t mask[16]; + + ui64 nCi; // Card random + ui64 nQ; // Reader random + ui64 nCh; // Reader challange + ui64 nCi_1; // Card anwser + + if ((argc != 2) && (argc != 5)) + { + printf("SecureMemory recovery - (c) Radboud University Nijmegen\n\n"); + printf("syntax: sma simulate\n"); + printf(" sma \n\n"); + return 1; + } + + printf(_CYAN_("\nAuthentication info\n\n")); + + // Check if this is a simulation + if (argc == 2) + { + // Generate random values for the key and randoms + srand((uint32_t)time(null)); + for (pos = 0; pos<8; pos++) + { + Gc[pos] = rand(); + Ci[pos] = rand(); + Q[pos] = rand(); + } + sm_auth(Gc,Ci,Q,Ch,Ci_1,&ostate); + printf(" Gc: "); print_bytes(Gc,8); + } else { + sscanf(argv[1],"%016llx",&nCi); num_to_bytes(nCi,8,Ci); + sscanf(argv[2],"%016llx",&nQ); num_to_bytes(nQ,8,Q); + sscanf(argv[3],"%016llx",&nCh); num_to_bytes(nCh,8,Ch); + sscanf(argv[4],"%016llx",&nCi_1); num_to_bytes(nCi_1,8,Ci_1); + printf(" Gc: unknown\n"); + } + + for (pos = 0; pos<8; pos++) + { + ks[2*pos] = Ci_1[pos]; + ks[(2*pos)+1] = Ch[pos]; + } + + printf(" Ci: "); print_bytes(Ci,8); + printf(" Q: "); print_bytes(Q,8); + printf(" Ch: "); print_bytes(Ch,8); + printf("Ci+1: "); print_bytes(Ci_1,8); + printf("\n"); + printf(" Ks: "); print_bytes(ks,16); + printf("\n"); + + printf("Initializing lookup tables for increasing cipher speed\n"); + init_lookup_left(); + init_lookup_right(); + init_lookup_left_substraction(); + init_lookup_right_substraction(); + + // Load in the ci (tag-nonce), together with the first half of Q (reader-nonce) + rstate_before_gc = 0; + lstate_before_gc = 0; + for (pos = 0; pos < 4; pos++) + { + next_right_fast(Ci[2*pos ],&rstate_before_gc); + next_right_fast(Ci[2*pos+1],&rstate_before_gc); + next_right_fast(Q[pos],&rstate_before_gc); + + next_left_fast(Ci[2*pos ],&lstate_before_gc); + next_left_fast(Ci[2*pos+1],&lstate_before_gc); + next_left_fast(Q[pos],&lstate_before_gc); + } + + printf("Determing the right states that correspond to the keystream\n"); + rbits = sm_right(ks,mask,&rstates); + printf("Top-bin for the right state contains " _GREEN_("%d")" correct bits\n",rbits); + printf("Total count of right bins: " _YELLOW_("%lu") "\n",(unsigned long)rstates.size()); + + if (rbits < 96) + { + printf(_RED_("\n WARNING!!! Better find another trace, the right top-bin is < 96 bits\n\n")); + } + + for(itrstates=rstates.begin();itrstates!=rstates.end();++itrstates) + { + rstate_after_gc = *itrstates; + sm_left_mask(ks,mask,rstate_after_gc); + printf("Using the state from the top-right bin: " _YELLOW_("0x%07llx")"\n",(unsigned long long)rstate_after_gc); + + search_gc_candidates_right(rstate_before_gc,rstate_after_gc,Q,&crstates); + printf("Found " _YELLOW_("%lu")" right candidates using the meet-in-the-middle attack\n",crstates.size()); + if (crstates.size() == 0) continue; + + printf("Calculating left states using the (unknown bits) mask from the top-right state\n"); + sm_left(ks,mask,&clstates); + printf("Found a total of " _YELLOW_("%lu")" left cipher states, recovering left candidates...\n",clstates.size()); + if (clstates.size() == 0) continue; + + search_gc_candidates_left(lstate_before_gc,Q,&clstates); + printf("The meet-in-the-middle attack returned " _YELLOW_("%lu")" left cipher candidates\n",clstates.size()); + if (clstates.size() == 0) continue; + + printf("Combining left and right states, disposing invalid combinations\n"); + combine_valid_left_right_states(&clstates,&crstates,&pgc_candidates); + + printf("Filtering the correct one using the middle part\n"); + for(itgc=pgc_candidates.begin();itgc!=pgc_candidates.end();++itgc) + { + num_to_bytes(*itgc,8,Gc_chk); + sm_auth(Gc_chk,Ci,Q,Ch_chk,Ci_1_chk,&ostate); + if ((memcmp(Ch_chk,Ch,8) == 0) && (memcmp(Ci_1_chk,Ci_1,8) == 0)) + { + printf("\nFound valid key: " _GREEN_("%016llX")"\n\n",(unsigned long long)*itgc); + return 0; + } + } + printf(_RED_("Could not find key using this right cipher state.\n\n")); + } + return 0; +} diff --git a/tools/cryptorf/sma_multi.cpp b/tools/cryptorf/sma_multi.cpp new file mode 100644 index 000000000..5c05e2e04 --- /dev/null +++ b/tools/cryptorf/sma_multi.cpp @@ -0,0 +1,1126 @@ +/* + * + * SecureMemory recovery Multithread + * + * Copyright (C) 2010, Flavio D. Garcia, Peter van Rossum, Roel Verdult + * and Ronny Wichers Schreur. Radboud University Nijmegen + * + * 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 . + * + * Modifed Iceman, 2020 + */ + +#include "defines.h" +#include "cryptolib.h" +#include "util.h" +#include +#include +#include +#include +#include +#include // sort, max_element, random_shuffle, remove_if, lower_bound +#include // greater, bind2nd +#include // std::thread +#include +#include +using namespace std; + +#ifdef _MSC_VER + #define inline __inline +#endif + +/* +>./sm 4f794a463ff81d81 ffffffffffffffff 1234567812345678 +SecureMemory simulator - (c) Radboud University Nijmegen + +Authenticate + Gc: 4f 79 4a 46 3f f8 1d 81 + Ci: ff ff ff ff ff ff ff ff + Q: 12 34 56 78 12 34 56 78 + Ch: 88 c9 d4 46 6a 50 1a 87 +Ci+1: de c2 ee 1b 1c 92 76 e9 + + Ks: de 88 c2 c9 ee d4 1b 46 1c 6a 92 50 76 1a e9 87 + + left: 1ddeac626 + right: 19aba45 + + left-candidates bins: + 004df8a64 (74) + 0059ff7d5 (81) + 00d2ff4ed (80) + 032df8b12 (78) + 0337b8b7d (87) + 036f7b607 (77) + 03a6f882a (79) + 03b2ff59b (76) + 04445c715 (74) + 0452175be (80) + 0b29f2a5b (78) + 0f6c834fb (76) + 0f78aac5b (75) + 0f79c8d49 (78) + 109691f61 (70) + 159d1687e (86) + 176e73456 (77) + 1ddeac626 (92) + 1facee6e5 (78) + 2049ed469 (80) + 205078bba (74) + 31c277406 (81) + 31c2777e6 (81) + 3770cdaf3 (74) + 48916e84e (77) + 4ba9b6520 (78) + 4ba9b653f (78) + 4c51c6463 (82) + 4c9432733 (76) + 4e3d88819 (81) + 4e3d88bf9 (81) + 51c8755b5 (76) + 5b2aeb858 (76) + 5fb612b96 (80) + 60531191a (78) + 6221539d9 (92) + 68918cba9 (79) + 6c9a11672 (78) + 6f696e09e (70) + 7086372b6 (78) + 7bade8a41 (82) + 7c90849f8 (77) + 7cc847482 (87) + +*/ + +const uint64_t left_candidates[43] = { + 0x6221539d9ull, 0x1ddeac626ull, 0x7cc847482ull, 0x0337b8b7dull, + 0x159d1687eull, 0x7bade8a41ull, 0x4c51c6463ull, 0x4e3d88bf9ull, + 0x4e3d88819ull, 0x31c2777e6ull, 0x31c277406ull, 0x0059ff7d5ull, + 0x5fb612b96ull, 0x2049ed469ull, 0x0452175beull, 0x00d2ff4edull, + 0x68918cba9ull, 0x03a6f882aull, 0x7086372b6ull, 0x6c9a11672ull, + 0x60531191aull, 0x4ba9b653full, 0x4ba9b6520ull, 0x1facee6e5ull, + 0x0f79c8d49ull, 0x0b29f2a5bull, 0x032df8b12ull, 0x7c90849f8ull, + 0x48916e84eull, 0x176e73456ull, 0x036f7b607ull, 0x5b2aeb858ull, + 0x51c8755b5ull, 0x4c9432733ull, 0x0f6c834fbull, 0x03b2ff59bull, + 0x0f78aac5bull, 0x3770cdaf3ull, 0x205078bbaull, 0x04445c715ull, + 0x004df8a64ull, 0x6f696e09eull, 0x109691f61ull +}; + +typedef struct { + uint64_t l; + uint64_t m; + uint64_t r; + nibble b0; + nibble b1; + nibble b1l; + nibble b1r; + nibble b1s; + bool invalid; + byte_t Gc[8]; +}cs_t; +typedef cs_t* pcs; + +typedef struct { + byte_t addition; + byte_t out; +} lookup_entry; + +enum cipher_state_side { + CSS_LEFT, + CSS_RIGHT +}; + +void print_cs(const char* text,pcs s) { + int pos; + + printf("%s",text); + + for(pos=6;pos>=0;pos--) + printf(" %02x",(byte_t)(s->l>>(pos*5))&0x1f); + + printf(" |"); + for(pos=6;pos>=0;pos--) + printf(" %02x",(byte_t)(s->m>>(pos*7))&0x7f); + + printf(" |"); + + for(pos=4;pos>=0;pos--) + printf(" %02x",(byte_t)(s->r>>(pos*5))&0x1f); + + printf("\n"); +} + +static inline byte_t mod(byte_t a, byte_t m) { + // Just return the input when this is less or equal than the modular value + if (a> (n_bits - 1))) & mask; +} + +static inline byte_t bit_rotate_r(byte_t a, byte_t n_bits) { + return ((a >> 1) | ((a&1) << (n_bits - 1))); +} +*/ + +#define BIT_ROL_MASK ((1 << 5) - 1) +#define BIT_ROL(a) ((((a) << 1) | ((a) >> 4)) & BIT_ROL_MASK) +#define BIT_ROR(a) (((a) >> 1) | (((a) & 1) << 4)) + + +static byte_t lookup_left_substraction[0x400]; +static byte_t lookup_right_subtraction[0x400]; +static lookup_entry lookup_left[0x100000]; +static lookup_entry lookup_right[0x8000]; +static byte_t left_addition[0x100000]; + +static inline void init_lookup_left() { + byte_t b3, b6, temp; + int i, index; + + for (i = 0; i <0x400; i++){ + b6 = i & 0x1f; + b3 = (i >> 5) & 0x1f; + index = (b3 << 15) | b6; + +// b6 = bit_rotate_l(b6, 5); + b6 = BIT_ROL(b6); + + temp = mod(b3 + b6,0x1f); + left_addition[index] = temp; + lookup_left[index].addition = temp; + lookup_left[index].out = ((temp^b3) & 0x0f); + } +} + +static inline void init_lookup_right() { + byte_t b16, b18, temp; + int i, index; + + for(i = 0; i <0x400; i++) { + b18 = i & 0x1f; + b16 = (i >> 5) & 0x1f; + index = (b16 << 10) | b18; + + temp = mod(b18 + b16,0x1f); + lookup_right[index].addition = temp; + lookup_right[index].out = ((temp^b16) & 0x0f); + } +} + +static void init_lookup_left_substraction() { + for(int index = 0; index < 0x400 ; index++) { + byte_t b3 = (index >> 5 & 0x1f); + byte_t bx = (index & 0x1f); + + //lookup_left_substraction[index] = bit_rotate_r(mod((bx+0x1f)-b3,0x1f),5); + lookup_left_substraction[index] = BIT_ROR( mod((bx + 0x1F) - b3, 0x1F)); + } +} + +static void init_lookup_right_substraction() { + for(int index = 0; index < 0x400 ; index++) { + int b16 = (index >>5); + byte_t bx = (index & 0x1f); + lookup_right_subtraction[index] = mod((bx + 0x1F) - b16, 0x1F); + } +} + +static inline void previous_left(byte_t in, vector *candidate_states) { + pcs state; + size_t size = candidate_states->size(); + for(size_t pos=0; posl >> 30) & 0x1f); + unsigned b3 = (unsigned)(state->l >> 5) & 0x3e0; + state->l = (state->l << 5); + + //Ignore impossible states + if (bx == 0) { + // Are we dealing with an impossible state? + if (b3 != 0) { + state->invalid = true; + } else { + // We only need to consider b6=0 + state->l &= 0x7ffffffe0ull; + state->l ^= (((uint64_t)in & 0x1f) << 20); + } + } else { + byte_t b6 = lookup_left_substraction[b3|bx]; + state->l = (state->l & 0x7ffffffe0ull) | b6; + state->l ^= (((uint64_t)in & 0x1f) << 20); + + // Check if we have a second candidate + if (b6 == 0x1f) { + cs_t nstate = *state; + nstate.l &= 0x7ffffffe0ull; + candidate_states->push_back(nstate); + } + } + } +} + +static inline void previous_right(byte_t in, vector *candidate_states) { + pcs state; + size_t size = candidate_states->size(); + for(size_t pos=0; posr >> 20) & 0x1f); + unsigned b16 = (unsigned)(state->r & 0x3e0);//(state->buffer_r >> 10) & 0x1f; + + state->r = (state->r << 5); + + // Ignore impossible states + if (bx == 0) { + if (b16 != 0) { + state->invalid=true; + } else { + // We only need to consider b18=0 + state->r &= 0x1ffffe0ull; + state->r ^= (((uint64_t)in & 0xf8) << 12); + } + } else { + byte_t b18 = lookup_right_subtraction[b16|bx]; + state->r = (state->r & 0x1ffffe0ull) | b18; + state->r ^= (((uint64_t)in & 0xf8) << 12); + //state->b_right = ((b14^b17) & 0x0f); + + // Check if we have a second candidate + if (b18 == 0x1f) { + cs_t nstate = *state; + nstate.r &= 0x1ffffe0ull; + candidate_states->push_back(nstate); + } + } + } +} + +static inline byte_t next_left_fast(byte_t in, uint64_t* left) { + if (in) + *left ^= ((in & 0x1f) << 20); + + lookup_entry* lookup = &(lookup_left[((*left) & 0xf801f)]); + *left = (((*left) >> 5)| ((uint64_t)lookup->addition << 30)); + return lookup->out; +} + +static inline byte_t next_left_ksbyte(uint64_t* left) { + lookup_entry* lookup; + byte_t bt; + + *left = (((*left) >> 5)| ((uint64_t)left_addition[((*left) & 0xf801f)] << 30)); + lookup = &(lookup_left[((*left) & 0xf801f)]); + *left = (((*left) >> 5)| ((uint64_t)lookup->addition << 30)); + bt = lookup->out << 4; + *left = (((*left) >> 5)| ((uint64_t)left_addition[((*left) & 0xf801f)] << 30)); + lookup = &(lookup_left[((*left) & 0xf801f)]); + *left = (((*left) >> 5)| ((uint64_t)lookup->addition << 30)); + bt |= lookup->out; + return bt; +} + +static inline byte_t next_right_fast(byte_t in, uint64_t* right) { + if (in) *right ^= ((in&0xf8) << 12); + lookup_entry* lookup = &(lookup_right[((*right) & 0x7c1f)]); + *right = (((*right) >> 5) | (lookup->addition << 20)); + return lookup->out; +} + +static inline void sm_left_mask(const byte_t* ks, byte_t* mask, uint64_t rstate) { + for (uint8_t pos = 0; pos < 16; pos++) { + next_right_fast(0,&rstate); + byte_t bt = next_right_fast(0,&rstate) << 4; + next_right_fast(0,&rstate); + bt |= next_right_fast(0,&rstate); + + // xor the bits with the keystream and count the "correct" bits + bt ^= ks[pos]; + + // Save the mask for the left produced bits + mask[pos] = bt; + } +} + + +std::atomic key_found{0}; +std::atomic key{0}; +std::atomic topbits{0}; +std::mutex g_ice_mtx; +uint32_t g_num_cpus = std::thread::hardware_concurrency(); + + +static void ice_sm_right_thread( + uint8_t offset, + uint8_t skips, + const byte_t* ks, + map* bincstates, + byte_t* mask + ) { + + byte_t tmp_mask[16]; + byte_t bt; + + for (uint64_t counter = offset; counter < 0x2000000; counter += skips) { + // Reset the current bitcount of correct bits + size_t bits = 0; + + // Copy the state we are going to test + uint64_t rstate = counter; + + for (uint8_t pos = 0; pos < 16; pos++) { + + next_right_fast(0, &rstate); + + bt = next_right_fast(0, &rstate) << 4; + + next_right_fast(0, &rstate); + + bt |= next_right_fast(0, &rstate); + + // xor the bits with the keystream and count the "correct" bits + bt ^= ks[pos]; + + // Save the mask for the left produced bits + tmp_mask[pos] = bt; + + // When the bit is xored away (=zero), it was the same, so correct ;) + if ((bt & 0x01) == 0) bits++; + if (((bt >> 1) & 0x01) == 0) bits++; + if (((bt >> 2) & 0x01) == 0) bits++; + if (((bt >> 3) & 0x01) == 0) bits++; + if (((bt >> 4) & 0x01) == 0) bits++; + if (((bt >> 5) & 0x01) == 0) bits++; + if (((bt >> 6) & 0x01) == 0) bits++; + if (((bt >> 7) & 0x01) == 0) bits++; + } + + g_ice_mtx.lock(); + if (bits > topbits.load(std::memory_order_relaxed)) { + // Copy the winning mask + topbits = bits; + memcpy(mask, tmp_mask, 16); + } + g_ice_mtx.unlock(); + + // Ignore states under 90 + if (bits >= 90) { + // Make sure the bits are used for ordering + g_ice_mtx.lock(); + if (bincstates->find((((uint64_t)bits) << 56) | counter) != bincstates->end()) + bincstates->at((((uint64_t)bits) << 56) | counter) = counter; + else + bincstates->insert( std::pair( (((uint64_t)bits) << 56) | counter, counter)); + g_ice_mtx.unlock(); + } + + if ((counter & 0xfffff) == 0) { + g_ice_mtx.lock(); + printf("."); + fflush(stdout); + g_ice_mtx.unlock(); + } + } +} +static uint32_t ice_sm_right(const byte_t* ks, byte_t* mask, vector* pcrstates) { + + uint32_t g_num_cpus = std::thread::hardware_concurrency(); + map bincstates; + topbits = ATOMIC_VAR_INIT(0); + + std::vector threads(g_num_cpus); + for (uint8_t m = 0; m < g_num_cpus; m++) { + threads[m] = std::thread( ice_sm_right_thread, m, g_num_cpus, ks, &bincstates, mask); + } + for (auto& t : threads) { + t.join(); + } + + printf("\n"); + + // Clear the candidate state vector + pcrstates->clear(); + + // Copy the order the states from lowest-bin to highest-bin + map::iterator it; + for (it = bincstates.begin(); it != bincstates.end(); ++it) { + pcrstates->push_back(it->second); + } + + // Reverse the vector order (so the higest bin comes first) + reverse(pcrstates->begin(), pcrstates->end()); + + return topbits; +} + +static void ice_sm_left_thread( + uint8_t offset, + uint8_t skips, + const byte_t* ks, + map* bincstates, + byte_t* mask + ) { + + size_t pos, bits; + byte_t correct_bits[16]; + byte_t bt; + lookup_entry* lookup; + + // Reset and initialize the cryptostate and vector + cs_t state; + memset(&state, 0x00, sizeof(cs_t)); + state.invalid = false; + + + for (uint64_t counter = offset; counter < 0x800000000ull; counter += skips) { + uint64_t lstate = counter; + + for (pos = 0; pos < 16; pos++) { + + lstate = (((lstate) >> 5)| ((uint64_t)left_addition[((lstate) & 0xf801f)] << 30)); + lookup = &(lookup_left[((lstate) & 0xf801f)]); + lstate = (((lstate) >> 5)| ((uint64_t)lookup->addition << 30)); + bt = lookup->out << 4; + lstate = (((lstate) >> 5)| ((uint64_t)left_addition[((lstate) & 0xf801f)] << 30)); + lookup = &(lookup_left[((lstate) & 0xf801f)]); + lstate = (((lstate) >> 5)| ((uint64_t)lookup->addition << 30)); + bt |= lookup->out; + + // xor the bits with the keystream and count the "correct" bits + bt ^= ks[pos]; + + // When the REQUIRED bits are NOT xored away (=zero), ignore this wrong state + if ((bt & mask[pos]) != 0) break; + + // Save the correct bits for statistical information + correct_bits[pos] = bt; + } + + // If we have parsed all 16 bytes of keystream, we have a valid CANDIDATE! + if (pos == 16) { + // Count the total correct bits + bits = 0; + for (pos = 0; pos < 16; pos++) { + // Get the next byte-value with correct bits + bt = correct_bits[pos]; + + // Count all the (correct) bits + // When the bit is xored away (=zero), it was the same, so correct ;) + if ((bt & 0x01) == 0) bits++; + if (((bt >> 1) & 0x01) == 0) bits++; + if (((bt >> 2) & 0x01) == 0) bits++; + if (((bt >> 3) & 0x01) == 0) bits++; + if (((bt >> 4) & 0x01) == 0) bits++; + if (((bt >> 5) & 0x01) == 0) bits++; + if (((bt >> 6) & 0x01) == 0) bits++; + if (((bt >> 7) & 0x01) == 0) bits++; + } + + state.l = counter; + + // Make sure the bits are used for ordering + g_ice_mtx.lock(); + printf("."); + fflush(stdout); + if (bincstates->find((((uint64_t)bits) << 56) | counter) != bincstates->end()) + bincstates->at((((uint64_t)bits) << 56) | counter) = state; + else + bincstates->insert( std::pair( (((uint64_t)bits) << 56) | counter, state)); + g_ice_mtx.unlock(); + + } + + if ((counter & 0xffffffffull) == 0) { + g_ice_mtx.lock(); + printf("%02.1f%%.", ((float)100/8) * (counter >> 32)); + fflush(stdout); + g_ice_mtx.unlock(); + } + } +} + +static void ice_sm_left(const byte_t* ks, byte_t* mask, vector* pcstates) { + + uint32_t g_num_cpus = std::thread::hardware_concurrency(); + + map bincstates; + std::vector threads(g_num_cpus); + for (uint8_t m = 0; m < g_num_cpus; m++) { + threads[m] = std::thread( ice_sm_left_thread, m, g_num_cpus, ks, &bincstates, mask); + } + + for (auto& t : threads) { + t.join(); + } + + printf("100%%\n"); + + // Clear the candidate state vector + pcstates->clear(); + + // Copy the order the states from lowest-bin to highest-bin + map::iterator it; + for(it = bincstates.begin(); it != bincstates.end(); ++it) { + pcstates->push_back(it->second); + } + // Reverse the vector order (so the higest bin comes first) + reverse(pcstates->begin(), pcstates->end()); +} + +static inline uint32_t sm_right(const byte_t* ks, byte_t* mask, vector* pcrstates) { + byte_t tmp_mask[16]; + size_t pos, bits, bit, topbits; + map bincstates; + map::iterator it; + byte_t bt; + topbits = 0; + + + for (uint64_t counter = 0; counter < 0x2000000; counter++) { + // Reset the current bitcount of correct bits + bits = 0; + + // Copy the state we are going to test + uint64_t rstate = counter; + + for (pos = 0; pos < 16; pos++) { + next_right_fast(0, &rstate); + bt = next_right_fast(0, &rstate) << 4; + next_right_fast(0, &rstate); + bt |= next_right_fast(0, &rstate); + + // xor the bits with the keystream and count the "correct" bits + bt ^= ks[pos]; + + // Save the mask for the left produced bits + tmp_mask[pos] = bt; + + for (bit = 0; bit < 8; bit++) { + // When the bit is xored away (=zero), it was the same, so correct ;) + if ((bt & 0x01) == 0) bits++; + bt >>= 1; + } + } + + if (bits > topbits) { + topbits = bits; + // Copy the winning mask + memcpy(mask, tmp_mask, 16); + } + + // Ignore states under 90 + if (bits >= 90) { + // Make sure the bits are used for ordering + bincstates[(((uint64_t)bits) << 56) | counter] = counter; + } + + if ((counter & 0xfffff) == 0) { + printf("."); + fflush(stdout); + } + } + printf("\n"); + + // Clear the candidate state vector + pcrstates->clear(); + + // Copy the order the states from lowest-bin to highest-bin + for (it = bincstates.begin(); it != bincstates.end(); ++it) { + pcrstates->push_back(it->second); + } + + // Reverse the vector order (so the higest bin comes first) + reverse(pcrstates->begin(), pcrstates->end()); + + return topbits; +} + +static inline void previous_all_input(vector *pcstates, uint32_t gc_byte_index, cipher_state_side css) { + byte_t btGc,in; + vector ncstates; + vector prev_ncstates; + vector::iterator it,itnew; + + // Loop through the complete entryphy of 5 bits for each candidate + // We ignore zero (xor 0x00) to avoid duplicates + for (btGc=0; btGc<0x20; btGc++) { + // Copy the original candidates that are supplied + ncstates = *pcstates; + + // Rollback the (candidate) cipher states with this input + if (css == CSS_RIGHT) { + in = btGc << 3; + previous_right(in,&ncstates); + } else { + in = btGc; + previous_left(in,&ncstates); + } + + for(itnew = ncstates.begin(); itnew != ncstates.end(); ++itnew) { + // Wipe away the invalid states + if (itnew->invalid == false) { + itnew->Gc[gc_byte_index] = in; + prev_ncstates.push_back(*itnew); + } + } + } + + // Copy the previous states into the vector + *pcstates = prev_ncstates; +} + +static inline void search_gc_candidates_right(const uint64_t rstate_before_gc, const uint64_t rstate_after_gc, const byte_t* Q, vector* pcstates) { + vector::iterator it; + vector csl_cand; + map matchbox; + map::iterator itmatch; + uint64_t rstate; + size_t counter; + cs_t state; + + // Generate 2^20 different (5 bits) values for the first 4 Gc bytes (0,1,2,3) + for (counter=0; counter<0x100000; counter++) { + rstate = rstate_before_gc; + next_right_fast((counter >> 12) & 0xf8,&rstate); + next_right_fast((counter >> 7) & 0xf8,&rstate); + next_right_fast(Q[4],&rstate); + next_right_fast((counter >> 2) & 0xf8,&rstate); + next_right_fast((counter << 3) & 0xf8,&rstate); + next_right_fast(Q[5],&rstate); + matchbox[rstate] = counter; + } + + // Reset and initialize the cryptostate and vecctor + memset(&state,0x00,sizeof(cs_t)); + state.invalid = false; + state.r = rstate_after_gc; + csl_cand.clear(); + csl_cand.push_back(state); + + // Generate 2^20(+splitting) different (5 bits) values for the last 4 Gc bytes (4,5,6,7) + previous_right(Q[7],&csl_cand); + previous_all_input(&csl_cand,7,CSS_RIGHT); + previous_all_input(&csl_cand,6,CSS_RIGHT); + previous_right(Q[6],&csl_cand); + previous_all_input(&csl_cand,5,CSS_RIGHT); + previous_all_input(&csl_cand,4,CSS_RIGHT); + + pcstates->clear(); + + // Take the intersection of the corresponding states ~2^15 values (40-25 = 15 bits) + for (it=csl_cand.begin();it!=csl_cand.end();++it) { + itmatch = matchbox.find(it->r); + if (itmatch != matchbox.end()) { + it->Gc[0] = (itmatch->second >> 12) & 0xf8; + it->Gc[1] = (itmatch->second >> 7) & 0xf8; + it->Gc[2] = (itmatch->second >> 2) & 0xf8; + it->Gc[3] = (itmatch->second << 3) & 0xf8; + + pcstates->push_back(*it); + } + } +} + +static inline void sm_left(const byte_t* ks, byte_t* mask, vector* pcstates) { + map bincstates; + map::iterator it; + uint64_t counter, lstate; + size_t pos, bits; + byte_t correct_bits[16]; + byte_t bt; + cs_t state; + lookup_entry* lookup; + + // Reset and initialize the cryptostate and vecctor + memset(&state, 0x00, sizeof(cs_t)); + state.invalid = false; + + for (counter = 0; counter < 0x800000000ull; counter++) { + lstate = counter; + + for (pos = 0; pos < 16; pos++) { + + lstate = (((lstate) >> 5)| ((uint64_t)left_addition[((lstate) & 0xf801f)] << 30)); + lookup = &(lookup_left[((lstate) & 0xf801f)]); + lstate = (((lstate) >> 5)| ((uint64_t)lookup->addition << 30)); + bt = lookup->out << 4; + lstate = (((lstate) >> 5)| ((uint64_t)left_addition[((lstate) & 0xf801f)] << 30)); + lookup = &(lookup_left[((lstate) & 0xf801f)]); + lstate = (((lstate) >> 5)| ((uint64_t)lookup->addition << 30)); + bt |= lookup->out; + + // xor the bits with the keystream and count the "correct" bits + bt ^= ks[pos]; + + // When the REQUIRED bits are NOT xored away (=zero), ignore this wrong state + if ((bt & mask[pos]) != 0) break; + + // Save the correct bits for statistical information + correct_bits[pos] = bt; + } + + // If we have parsed all 16 bytes of keystream, we have a valid CANDIDATE! + if (pos == 16) { + // Count the total correct bits + bits = 0; + for (pos = 0; pos < 16; pos++) { + // Get the next byte-value with correct bits + bt = correct_bits[pos]; + + // Count all the (correct) bits + // When the bit is xored away (=zero), it was the same, so correct ;) + if ((bt & 0x01) == 0) bits++; + if (((bt >> 1) & 0x01) == 0) bits++; + if (((bt >> 2) & 0x01) == 0) bits++; + if (((bt >> 3) & 0x01) == 0) bits++; + if (((bt >> 4) & 0x01) == 0) bits++; + if (((bt >> 5) & 0x01) == 0) bits++; + if (((bt >> 6) & 0x01) == 0) bits++; + if (((bt >> 7) & 0x01) == 0) bits++; + + } + + // Print the left candidate + // printf("%09llx (%d)\n",counter,bits); + printf("."); + fflush(stdout); + + state.l = counter; + // Make sure the bits are used for ordering + bincstates[(((uint64_t)bits)<<56) | counter] = state; + } + + if ((counter & 0xffffffffull) == 0) { + printf("%02.1f%%.", ((float)100/8)*(counter>>32)); + fflush(stdout); + } + } + + printf("100%%\n"); + + // Clear the candidate state vector + pcstates->clear(); + + // Copy the order the states from lowest-bin to highest-bin + for(it = bincstates.begin(); it != bincstates.end(); ++it) { + pcstates->push_back(it->second); + } + // Reverse the vector order (so the higest bin comes first) + reverse(pcstates->begin(), pcstates->end()); +} + +static inline void search_gc_candidates_left(const uint64_t lstate_before_gc, const byte_t* Q, vector* pcstates) { + vector csl_cand,csl_search; + vector::iterator itsearch,itcand; + map matchbox; + map::iterator itmatch; + uint64_t lstate; + size_t counter; + + // Generate 2^20 different (5 bits) values for the first 4 Gc bytes (0,1,2,3) + for (counter=0; counter < 0x100000; counter++) { + lstate = lstate_before_gc; + next_left_fast((counter >> 15) & 0x1f, &lstate); + next_left_fast((counter >> 10) & 0x1f, &lstate); + next_left_fast(Q[4], &lstate); + next_left_fast((counter >> 5) & 0x1f, &lstate); + next_left_fast(counter & 0x1f, &lstate); + next_left_fast(Q[5], &lstate); + matchbox[lstate] = counter; + } + + // Copy the input candidate states and clean the output vector + csl_cand = *pcstates; + pcstates->clear(); + + for (itcand = csl_cand.begin(); itcand != csl_cand.end(); ++itcand) { + csl_search.clear(); + csl_search.push_back(*itcand); + + // Generate 2^20(+splitting) different (5 bits) values for the last 4 Gc bytes (4,5,6,7) + previous_left(Q[7], &csl_search); + previous_all_input(&csl_search, 7, CSS_LEFT); + previous_all_input(&csl_search, 6, CSS_LEFT); + previous_left(Q[6], &csl_search); + previous_all_input(&csl_search, 5, CSS_LEFT); + previous_all_input(&csl_search, 4, CSS_LEFT); + + // Take the intersection of the corresponding states ~2^15 values (40-25 = 15 bits) + for(itsearch = csl_search.begin(); itsearch != csl_search.end(); ++itsearch) { + itmatch = matchbox.find(itsearch->l); + if (itmatch != matchbox.end()){ + itsearch->Gc[0] = (itmatch->second >> 15) & 0x1f; + itsearch->Gc[1] = (itmatch->second >> 10) & 0x1f; + itsearch->Gc[2] = (itmatch->second >> 5) & 0x1f; + itsearch->Gc[3] = itmatch->second & 0x1f; + + pcstates->push_back(*itsearch); + } + } + printf("."); + fflush(stdout); + } + printf("\n"); +} + +void combine_valid_left_right_states(vector* plcstates, vector* prcstates, vector* pgc_candidates) { + vector::iterator itl, itr; + size_t pos,count; + uint64_t gc; + bool valid; + + vector outer, inner; + if ( plcstates->size() > prcstates->size()) { + outer = *plcstates; + inner = *prcstates; + } else { + outer = *prcstates; + inner = *plcstates; + } + + printf("Outer " _YELLOW_("%lu")" , inner " _YELLOW_("%lu") "\n", outer.size(), inner.size()); + + // Clean up the candidate list + pgc_candidates->clear(); + count = 0; + for( itl = outer.begin(); itl != outer.end(); ++itl) { + for(itr = inner.begin(); itr != inner.end(); ++itr) { + valid = true; + // Check for left and right candidates that share the overlapping bits (8 x 2bits of Gc) + for (pos = 0; pos < 8; pos++) { + if ((itl->Gc[pos] & 0x18) != (itr->Gc[pos] & 0x18)) { + valid = false; + break; + } + } + + if (valid) { + gc = 0; + for (pos = 0; pos < 8; pos++) { + gc <<= 8; + gc |= (itl->Gc[pos] | itr->Gc[pos]); + } + + pgc_candidates->push_back(gc); + } + count++; + } + } + printf("Found a total of " _YELLOW_("%llu")" combinations, ",((unsigned long long)plcstates->size()) * prcstates->size()); + printf("but only " _GREEN_("%lu")" were valid!\n", pgc_candidates->size()); +} + +static void ice_compare( + uint8_t offset, + uint8_t skips, + vector* candidates, + crypto_state_t* ostate, + byte_t *Ci, + byte_t *Q, + byte_t *Ch, + byte_t *Ci_1 + ) { + byte_t Gc_chk[8]; + byte_t Ch_chk[ 8]; + byte_t Ci_1_chk[ 8]; + + for (std::size_t i = offset; i < candidates->size(); i += skips) { + if (key_found.load(std::memory_order_relaxed)) + break; + + uint64_t tkey = candidates->at(i); + num_to_bytes(tkey, 8, Gc_chk); + + sm_auth(Gc_chk, Ci, Q, Ch_chk, Ci_1_chk, ostate); + if ((memcmp(Ch_chk, Ch, 8) == 0) && (memcmp(Ci_1_chk, Ci_1, 8) == 0)) { + key_found = true; + key = tkey; + break; + } + } + return; +} + + +int main(int argc, const char* argv[]) { + size_t pos; + crypto_state_t ostate; + uint64_t rstate_before_gc, rstate_after_gc; + uint64_t lstate_before_gc; + vector rstates, lstates_after_gc, pgc_candidates; + vector::iterator itrstates, itgc; + vector crstates; + vector clcandidates, clstates; + vector::iterator it; + uint32_t rbits; + + // byte_t Gc[ 8] = {0x4f,0x79,0x4a,0x46,0x3f,0xf8,0x1d,0x81}; + // byte_t Gc[ 8] = {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; + // byte_t Ci[ 8] = {0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff}; + // byte_t Q[ 8] = {0x12,0x34,0x56,0x78,0x12,0x34,0x56,0x78}; + byte_t Gc[ 8]; + byte_t Ci[ 8]; + byte_t Q[ 8]; + byte_t Ch[ 8]; + byte_t Ci_1[ 8]; + + // byte_t ks[16] = {0xde,0x88,0xc2,0xc9,0xee,0xd4,0x1b,0x46,0x1c,0x6a,0x92,0x50,0x76,0x1a,0xe9,0x87}; + // byte_t mask[16] = {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; + // byte_t mask[16] = {0x04,0xb0,0xe1,0x10,0xc0,0x33,0x44,0x20,0x20,0x00,0x70,0x8c,0x22,0x04,0x10,0x80}; + + byte_t ks[16]; + byte_t mask[16]; + + ui64 nCi; // Card random + ui64 nQ; // Reader random + ui64 nCh; // Reader challange + ui64 nCi_1; // Card anwser + + if ((argc != 2) && (argc != 5)) { + printf("SecureMemory recovery - (c) Radboud University Nijmegen\n\n"); + printf("syntax: sma_multi simulate\n"); + printf(" sma_multi \n\n"); + return 1; + } + + printf(_CYAN_("\nAuthentication info\n\n")); + + // Check if this is a simulation + if (argc == 2) { + // Generate random values for the key and randoms + srand((uint32_t)time(null)); + for (pos = 0; pos<8; pos++) { + Gc[pos] = rand(); + Ci[pos] = rand(); + Q[pos] = rand(); + } + sm_auth(Gc,Ci,Q,Ch,Ci_1,&ostate); + printf(" Gc: "); print_bytes(Gc,8); + } else { + sscanf(argv[1],"%016llx",&nCi); num_to_bytes(nCi,8,Ci); + sscanf(argv[2],"%016llx",&nQ); num_to_bytes(nQ,8,Q); + sscanf(argv[3],"%016llx",&nCh); num_to_bytes(nCh,8,Ch); + sscanf(argv[4],"%016llx",&nCi_1); num_to_bytes(nCi_1,8,Ci_1); + printf(" Gc: unknown\n"); + } + + for (pos = 0; pos<8; pos++) { + ks[2*pos] = Ci_1[pos]; + ks[(2*pos)+1] = Ch[pos]; + } + + printf(" Ci: "); print_bytes(Ci,8); + printf(" Q: "); print_bytes(Q,8); + printf(" Ch: "); print_bytes(Ch,8); + printf("Ci+1: "); print_bytes(Ci_1,8); + printf("\n"); + printf(" Ks: "); print_bytes(ks,16); + printf("\n"); + + printf("\nMultithreaded, will use " _YELLOW_("%u") " threads\n", g_num_cpus); + printf("Initializing lookup tables for increasing cipher speed\n"); + + std::thread foo_left(init_lookup_left); + std::thread foo_right(init_lookup_right); + std::thread foo_leftsub(init_lookup_left_substraction); + std::thread foo_rightsub(init_lookup_right_substraction); + + foo_left.join(); + foo_right.join(); + foo_leftsub.join(); + foo_rightsub.join(); + + // Load in the ci (tag-nonce), together with the first half of Q (reader-nonce) + rstate_before_gc = 0; + lstate_before_gc = 0; + + for (pos = 0; pos < 4; pos++) { + next_right_fast(Ci[2*pos ], &rstate_before_gc); + next_right_fast(Ci[2*pos+1], &rstate_before_gc); + next_right_fast(Q[pos], &rstate_before_gc); + + next_left_fast(Ci[2*pos ], &lstate_before_gc); + next_left_fast(Ci[2*pos+1], &lstate_before_gc); + next_left_fast(Q[pos], &lstate_before_gc); + } + + printf("Determing the right states that correspond to the keystream\n"); + //rbits = sm_right(ks, mask, &rstates); + rbits = ice_sm_right(ks, mask, &rstates); + + printf("Top-bin for the right state contains " _GREEN_("%d")" correct bits\n", rbits); + printf("Total count of right bins: " _YELLOW_("%lu") "\n", (unsigned long)rstates.size()); + + if (rbits < 96) { + printf(_RED_("\n WARNING!!! Better find another trace, the right top-bin is < 96 bits\n\n")); + } + + for (itrstates = rstates.begin(); itrstates != rstates.end(); ++itrstates) { + rstate_after_gc = *itrstates; + sm_left_mask(ks, mask, rstate_after_gc); + printf("Using the state from the top-right bin: " _YELLOW_("0x%07llx")"\n", (unsigned long long)rstate_after_gc); + + search_gc_candidates_right(rstate_before_gc, rstate_after_gc, Q, &crstates); + printf("Found " _YELLOW_("%lu")" right candidates using the meet-in-the-middle attack\n", crstates.size()); + if (crstates.size() == 0) continue; + + printf("Calculating left states using the (unknown bits) mask from the top-right state\n"); + //sm_left(ks, mask, &clstates); + ice_sm_left(ks, mask, &clstates); + + printf("Found a total of " _YELLOW_("%lu")" left cipher states, recovering left candidates...\n", clstates.size()); + if (clstates.size() == 0) continue; + search_gc_candidates_left(lstate_before_gc, Q, &clstates); + + + printf("The meet-in-the-middle attack returned " _YELLOW_("%lu")" left cipher candidates\n", clstates.size()); + if (clstates.size() == 0) continue; + + printf("Combining left and right states, disposing invalid combinations\n"); + combine_valid_left_right_states(&clstates, &crstates, &pgc_candidates); + + printf("Filtering the correct one using the middle part\n"); + + + key_found = ATOMIC_VAR_INIT(false); + + std::vector threads(g_num_cpus); + for (uint8_t m = 0; m < g_num_cpus; m++) { + threads[m] = std::thread( ice_compare, m, g_num_cpus, &pgc_candidates, &ostate, ref(Ci), ref(Q), ref(Ch), ref(Ci_1) ); + } + + for (auto& t : threads) { + t.join(); + } + + if (key_found) { + printf("\nFound valid key: " _GREEN_("%016lX")"\n\n", key.load()); + break; + } + + printf(_RED_("\nCould not find key using this right cipher state.\n\n")); + } + return 0; +} diff --git a/tools/cryptorf/test.sh b/tools/cryptorf/test.sh new file mode 100755 index 000000000..5fe828689 --- /dev/null +++ b/tools/cryptorf/test.sh @@ -0,0 +1,9 @@ +#!/bin/sh + +# harder test +time ./sma c2fa94a5231d14e1 d291eeef5f76e6df 586385693a9b0f2c ec9aba404505b0fa +time ./sma_multi c2fa94a5231d14e1 d291eeef5f76e6df 586385693a9b0f2c ec9aba404505b0fa + +# simpler +time ./sma ffffffffffffffff 1234567812345678 88c9d4466a501a87 dec2ee1b1c9276e9 +time ./sma_multi ffffffffffffffff 1234567812345678 88c9d4466a501a87 dec2ee1b1c9276e9 diff --git a/tools/cryptorf/util.c b/tools/cryptorf/util.c new file mode 100644 index 000000000..4da252ab5 --- /dev/null +++ b/tools/cryptorf/util.c @@ -0,0 +1,23 @@ +#include "util.h" +#include + +void num_to_bytes(uint64_t n, size_t len, byte_t* dst) +{ + while (len--) + { + dst[len] = (byte_t)n; + n >>= 8; + } +} + +void print_bytes(const byte_t* pbtData, const size_t szLen) { + size_t uiPos; + for (uiPos=0; uiPos < szLen; uiPos++) { + printf("%02x ",pbtData[uiPos]); + if (uiPos>20){ + printf("..."); + break; + } + } + printf("\n"); +} diff --git a/tools/cryptorf/util.h b/tools/cryptorf/util.h new file mode 100644 index 000000000..68a16ecf1 --- /dev/null +++ b/tools/cryptorf/util.h @@ -0,0 +1,43 @@ +/* + * + * Various Utilities + * + * Copyright (C) 2010, Flavio D. Garcia, Peter van Rossum, Roel Verdult + * and Ronny Wichers Schreur. Radboud University Nijmegen + * + * 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 _UTIL_H_ +#define _UTIL_H_ + +#include +#include "defines.h" + + +#define AEND "\x1b[0m" + +#define _BLUE_(s) "\x1b[34m" s AEND +#define _RED_(s) "\x1b[31m" s AEND +#define _GREEN_(s) "\x1b[32m" s AEND +#define _YELLOW_(s) "\x1b[33m" s AEND +#define _MAGENTA_(s) "\x1b[35m" s AEND +#define _CYAN_(s) "\x1b[36m" s AEND +#define _WHITE_(s) "\x1b[37m" s AEND + +void num_to_bytes(uint64_t n, size_t len, byte_t* dst); +void print_bytes(const byte_t* pbtData, const size_t szLen); + +#endif // _UTIL_H_