added the securememory simulator_recovery code by @Roel et al

This commit is contained in:
iceman1001 2020-08-19 15:24:32 +02:00
commit 073e79553e
13 changed files with 3062 additions and 0 deletions

30
tools/cryptorf/Makefile Normal file
View file

@ -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

89
tools/cryptorf/cm.c Normal file
View file

@ -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 <http://www.gnu.org/licenses/>.
*
*/
#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 <Gc> <Ci> <Q> <Q(s)>\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;
}

294
tools/cryptorf/crf.c Normal file
View file

@ -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 <http://www.gnu.org/licenses/>.
*
*/
#include "defines.h"
#include <stdio.h>
#include <time.h>
#include <nfc/nfc.h>
#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)<len) {
printf("%02x ",ct[count+pos]);
} else {
printf(" ");
}
}
printf(" => ");
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 <Gc0> <zone> <offset> <len> [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;
}

359
tools/cryptorf/cryptolib.c Normal file
View file

@ -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 <http://www.gnu.org/licenses/>.
*
*/
#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<m) return a;
// Compute the modular value
a %= m;
// Return the funny value, when the output was now zero, return the modular value
return (a == 0) ? m : a;
}
byte_t bit_rotate_left(byte_t a, byte_t n_bits)
{
// Rotate value a with the length of n_bits only 1 time
byte_t mask = (1 << n_bits) - 1;
return ((a << 1) | (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<len; pos++)
{
// Perform the crypto operation
bt = in[pos] ^ cm_byte(s);
// Generate output
if (out) out[pos] = bt;
// Detect where to find the plaintext for loading into cipher state
if (ca == CA_DECRYPT)
{
next(true,bt,s);
} else {
next(true,in[pos],s);
}
// Shift the cipher state 5 times
next_n(true,5,0,s);
}
}
void cm_encrypt(const byte_t offset, const byte_t len, const byte_t* ct, byte_t* pt, crypto_state s) {
next_n(true, 5, 0, s);
next(true, 0, s);
cm_crypt(CA_ENCRYPT, offset, len, ct, pt, s);
}
void cm_decrypt(const byte_t offset, const byte_t len, const byte_t* ct, byte_t* pt, crypto_state s) {
next_n(true, 5, 0, s);
next(true, 0, s);
cm_crypt(CA_DECRYPT, offset, len, ct, pt, s);
}
void cm_grind_read_system_zone(const byte_t offset, const byte_t len, const byte_t* pt, crypto_state s) {
cm_crypt(CA_ENCRYPT, offset, len, pt, null, s);
}
void cm_grind_set_user_zone(const byte_t zone, crypto_state s) {
next(true, zone, s);
}
void cm_mac(byte_t* mac, crypto_state s) {
next_n(true,10,0,s);
if (mac)
mac[0] = cm_byte(s);
next_n(true,5,0,s);
if (mac)
mac[1] = cm_byte(s);
}
void cm_password(const byte_t* pt, byte_t* ct, crypto_state s) {
for (size_t pos = 0; pos < 3; pos++) {
next_n(true, 5, pt[pos], s);
ct[pos] = cm_byte(s);
}
}

View file

@ -0,0 +1,56 @@
/*
*
* 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 <http://www.gnu.org/licenses/>.
*
*/
#ifndef _CRYPTOLIB_H_
#define _CRYPTOLIB_H_
#include "defines.h"
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
// 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_

36
tools/cryptorf/defines.h Normal file
View file

@ -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 <http://www.gnu.org/licenses/>.
*
*/
#ifndef _DEFINES_H_
#define _DEFINES_H_
#define _CRT_SECURE_NO_WARNINGS
#define _CRT_SECURE_NO_DEPRECATE
#include <stdint.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdio.h>
#define null 0
typedef unsigned char byte_t;
typedef long long unsigned int ui64;
#endif // _DEFINES_H_

2
tools/cryptorf/laundry.sh Executable file
View file

@ -0,0 +1,2 @@
#!/bin/sh
./crf 4f794a463ff81d81 2 0 80 546dae

77
tools/cryptorf/sm.c Normal file
View file

@ -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 <http://www.gnu.org/licenses/>.
*
*/
#include "defines.h"
#include "cryptolib.h"
#include "util.h"
#include <stdio.h>
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 <Gc> <Ci> <Q>\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;
}

918
tools/cryptorf/sma.cpp Normal file
View file

@ -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 <http://www.gnu.org/licenses/>.
*
* Modified Iceman, 2020
*/
#include "defines.h"
#include "cryptolib.h"
#include "util.h"
#include <stdio.h>
#include <time.h>
#include <iostream>
#include <vector>
#include <map>
#include <algorithm> // sort, max_element, random_shuffle, remove_if, lower_bound
#include <functional> // 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<m) return a;
// Compute the modular value
a %= m;
// Return the funny value, when the output was now zero, return the modular value
return (a == 0) ? m : a;
}
static inline byte_t bit_rotate_l(byte_t a, byte_t n_bits)
{
// Rotate value a with the length of n_bits only 1 time
byte_t mask = (1 << n_bits) - 1;
return ((a << 1) | (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<cs_t> *candidate_states)
{
pcs state;
size_t size = candidate_states->size();
for(size_t pos=0; pos<size; pos++)
{
state = &((*candidate_states)[pos]);
byte_t bx = (byte_t)((state->l >> 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<cs_t> *candidate_states)
{
pcs state;
size_t size = candidate_states->size();
for(size_t pos=0; pos<size; pos++)
{
state = &((*candidate_states)[pos]);
byte_t bx = (byte_t)((state->r >> 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<uint64_t>* pcrstates)
{
byte_t tmp_mask[16];
size_t pos,bits,bit,topbits;
uint64_t rstate,counter;
map<uint64_t,uint64_t> bincstates;
map<uint64_t,uint64_t>::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<cs_t> *pcstates, uint32_t gc_byte_index, cipher_state_side css)
{
byte_t btGc,in;
vector<cs_t> ncstates;
vector<cs_t> prev_ncstates;
vector<cs_t>::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<cs_t>* pcstates)
{
vector<cs_t>::iterator it;
vector<cs_t> csl_cand;
map<uint64_t,uint64_t> matchbox;
map<uint64_t,uint64_t>::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<cs_t>* pcstates)
{
map<uint64_t,cs_t> bincstates;
map<uint64_t,cs_t>::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<cs_t>* pcstates)
{
vector<cs_t> csl_cand,csl_search;
vector<cs_t>::iterator itsearch,itcand;
map<uint64_t,uint64_t> matchbox;
map<uint64_t,uint64_t>::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<cs_t>* plcstates, vector<cs_t>* prcstates, vector<uint64_t>* pgc_candidates)
{
vector<cs_t>::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<uint64_t> rstates,lstates_after_gc,pgc_candidates;
vector<uint64_t>::iterator itrstates,itgc;
vector<cs_t> crstates;
vector<cs_t> clcandidates,clstates;
vector<cs_t>::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 <Ci> <Q> <Ch> <Ci+1>\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;
}

1126
tools/cryptorf/sma_multi.cpp Normal file

File diff suppressed because it is too large Load diff

9
tools/cryptorf/test.sh Executable file
View file

@ -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

23
tools/cryptorf/util.c Normal file
View file

@ -0,0 +1,23 @@
#include "util.h"
#include <stdio.h>
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");
}

43
tools/cryptorf/util.h Normal file
View file

@ -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 <http://www.gnu.org/licenses/>.
*
*/
#ifndef _UTIL_H_
#define _UTIL_H_
#include <stdint.h>
#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_