Move to crypto1_bs directory

This commit is contained in:
2023-04-16 18:27:21 +08:00
parent d42eb52a2e
commit b867c7d07c
20 changed files with 3 additions and 4 deletions

2
crypto1_bs/.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
holycard_collect
holycard_solve

26
crypto1_bs/Makefile Executable file
View File

@ -0,0 +1,26 @@
# if -march=native does not work on your platform, you could try
# -msse
# -msse2
# -mavx
# or
# -mavx2
CC = gcc
CFLAGS = -std=gnu99 -O3 -march=native
LDFLAGS= #-Wl,--allow-multiple-definition
all: holycard_collect holycard_solve
CRAPTEV1 = craptev1-v1.1/craptev1.c -I craptev1-v1.1/
CRAPTO1 = crapto1-v3.3/crapto1.c crapto1-v3.3/crypto1.c -I crapto1-v3.3/
CRYPTO1_BS = crypto1_bs.c crypto1_bs_crack.c
holycard_collect:
$(CC) $(CFLAGS) $@.c $(CRYPTO1_BS) $(CRAPTO1) ${CRAPTEV1} -o $@ -lpthread -lnfc -lm $(LDFLAGS)
holycard_solve:
$(CC) $(CFLAGS) $@.c $(CRYPTO1_BS) $(CRAPTO1) ${CRAPTEV1} -o $@ -lpthread -lm $(LDFLAGS)
clean:
rm -f solve.so holycard_collect holycard_solve

81
crypto1_bs/README.md Normal file
View File

@ -0,0 +1,81 @@
Bitsliced Crypto-1 brute-forcer
===============================
A pure C(99) implementation of the [Crypto-1 cipher](https://en.wikipedia.org/wiki/Crypto-1) using the method of [bitslicing](https://en.wikipedia.org/wiki/Bit_slicing), which uses GNU vector extensions to be portable across SSE/AVX/AVX2 supporting architectures while offering the highest amount of possible parallelism.
Background
----------
I wrote this as a patch for [piwi's imlementation](https://github.com/pwpiwi/proxmark3/tree/hard_nested/) of the research documented in [Ciphertext-only cryptanalysis on hardened Mifare Classic cards](http://www.cs.ru.nl/~rverdult/Ciphertext-only_Cryptanalysis_on_Hardened_Mifare_Classic_Cards-CCS_2015.pdf) after reading (most of) the paper, while it was still under [active development](http://www.proxmark.org/forum/viewtopic.php?id=2120).
After my development of a somewhat naive brute-forcer, another implementation of the same attack surfaced, [CraptEV1](http://crapto1.netgarage.org/).
With all of its clever tricks that code pushed me to improve my own, eventually reaching a significant (8-10x) speedup.
Besides that, its publication as a library also inspired the included libnfc application that demonstrates the CraptEV1 attack code + my bitsliced cracker.
The proxmark3 patch, as well as the code for piwi's branch that it applies to, have since been merged into [Iceman1001's fork](https://github.com/iceman1001/proxmark3/) along with many other experimental improvements to the proxmark3 codebase.
Much more work has been done in the project since my patch, so I would advise proxmark3 users to use that fork to test the code.
My original patch is included for completeness as `pwpiwi_proxmark3_hard_nested.patch`.
Even later still, this code actually got merged into the upstream [Proxmark3 codebase](https://github.com/Proxmark/proxmark3)! More importantly, it was included as part of an entirely new variation on the attack by [@pwpiwi](https://github.com/pwpiwi/), which requires fewer nonces and achieves a better reduction of potential states, while also improving the brute-forcing phase, ending up as another order of magnitude faster in practice.
This improved solution is also usable with a branch of [mfoc](https://github.com/vk496/mfoc/tree/hardnested) by [@vk496](https://github.com/vk496).
Tools
-----
~~The following tools are only available for / tested on 64-bit Linux.~~
Support for 32-bit and 64-bit machines was tested on various Intel/ARM based Linux platforms.
OSX compatibility issues were resolved by [@unkernet](https://github.com/unkernet/).
If you want to use the following stand-alone binaries, you will need the original CraptEV1 / Crapto1 source packages.
For convenience, and because redistribution of CraptEV1 is not allowed, I've added make targets `get_craptev1` and `get_crapto1` to fetch and extract these packages to the current working directory.
I have included a conversion of the test file `0xcafec0de.txt` included in the CraptEV1 package to the binary format used by the `proxmark3/hard_nested` branch.
`solve_bs` is analogous to CraptEV1 `solve` and works on .txt files using the bitsliced crypto-1 cracker
$ ./solve_bs craptev1-v1.1/0xcafec0de.txt 0xcafec0de
`solve_piwi` uses CraptEV1 on .bin files as gathered by piwi's PM3 code
$ ./solve_piwi 0xcafec0de.bin
`solve_piwi_bs` does the same but uses the bitsliced cracker
$ ./solve_piwi_bs 0xcafec0de.bin
`libnfc_crypto1_crack` uses libnfc to demonstrate the CraptEV1 code using the bitsliced cracker
$ ./libnfc_crypto1_crack 000000000000 0 A 4 A
Acknowledgements
----------------
Special thanks to Carlo Meijer, Roel Verdult, piwi and bla.
License
-------
All the code in this repository is made available under the MIT license, except for the files `pwpiwi_proxmark3_hard_nested.patch` and `libnfc_crypto1_crack.c`, which are GPLv2 due to deriving from respectively Proxmark3 and MFOC.
Copyright (c) 2015-2016 Aram Verstegen
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,2 @@
all: solve.c craptev1.c craptev1.h
gcc -O3 -mpopcnt solve.c craptev1.c -o solve

View File

@ -0,0 +1,472 @@
/**
* CraptEV1
* Copyright (c) 2015-2016 blapost@gmail.com
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted for non-commercial use only.
*
* No redistribution. No modifications.
*/
#include "craptev1.h"
static uint8_t halfsum[2][1 << 20];
static uint8_t filterflip[1 << 20];
static uint8_t filterlut[1 << 20];
static uint32_t hsum_off[2][0x89];
static double prob[257];
void __attribute__((constructor)) craptev1_init() {
uint32_t i, j, s, t, p, q;
uint32_t esum, osum;
uint64_t ocnt[9] = {0}, ecnt[9] = {0};
if(**halfsum)
return;
for(i = 0; i < 1 << 20; i++) {
osum = esum = 0;
for(j = 0; j < 1 << 4; j++) {
s = i << 4 | j;
t = filter(s) ^ filter(s >> 1) ^ filter(s >> 2) ^ filter(s >> 3);
osum += t ^ filter(i);
esum += t;
}
halfsum[0][i] = esum >> 1;
halfsum[1][i] = osum >> 1;
ecnt[esum >> 1]++;
ocnt[osum >> 1]++;
filterflip[i] = filter(i) ^ filter(i ^ 1);
filterlut[i] = filter(i);
}
for(p = 0; p < 9; ++p)
for(q = 0; q < 9; ++q)
prob[8 * (4 * p + 4 * q - p * q)] += ecnt[p] * ocnt[q];
for(i = 0; i < 257; ++i)
prob[i] /= 1ull << 40;
for(j = 0; j < 1 << 4; ++j)
for(i = 0; i < 1 << 20; ++i) {
hsum_off[0][halfsum[0][j << 16 | i >> 4] << 4 | halfsum[0][i]]++;
hsum_off[1][halfsum[1][j << 16 | i >> 4] << 4 | halfsum[1][i]]++;
}
}
#define filter(x) (filterlut[(x) & 0xfffff])
#define LF_POLY (0x8708040029CE5C)
#define ROR(x, n) ((x) >> (n) | (x) << (32 - (n)))
#define DIVIDE(s, p) ROR((unsigned)(((int)(s - (p) * 32)) / (int)(4 - (p))), 3)
#define FACTOR(s, p) ((s & 1) || ((p) == 4 ? s == 128 : DIVIDE(s, (p)) < 9))
/** getsum0
* Calculate the sum property at time zero
*/
uint32_t getsum0(uint64_t *nonce) {
uint32_t unique[256] = {0};
uint32_t i, numfound = 0 , sum = 0;
for(i = 0; nonce[i] != -1 && numfound < 256; ++i)
if(!unique[0xff & nonce[i]]) {
sum += parity(0xff & nonce[i]) ^ BIT(nonce[i], 32);
unique[0xff & nonce[i]] = 1;
numfound++;
}
return numfound == 256 ? sum : -1;
}
/** eliminate
* build initial sorted candidate list based on sumproperties
*/
uint32_t* eliminate(uint32_t sum0, uint32_t sum8, uint32_t isodd) {
uint32_t y, yy, *set, p, r, *wrt[0x89] = {0}, *w, irr8 = sum8 >> 1 == 64;
uint8_t *hsum = halfsum[isodd], i, irr0 = sum0 >> 1 == 64;
set = w = malloc((sizeof(uint32_t) << 24) + 4);
for(p = 0; p != 4 && !irr0; p = (p + 1) * 2 % 11)
for(r = 0; r != 4; r = (r + 1) * 2 % 11)
if(FACTOR(sum0, p) && FACTOR(sum8, r))
w = (wrt[p << 4 | r] = w) + hsum_off[isodd][p << 4 | r];
for(r = 0; r != 4 && irr0; r = (r + 1) * 2 % 11)
for(p = 0; p != 4; p = (p + 1) * 2 % 11)
if(FACTOR(sum0, p) && FACTOR(sum8, r))
w = (wrt[p << 4 | r] = w) + hsum_off[isodd][p << 4 | r];
for(p = 0; p != 4; p = (p + 1) * 2 % 11)
if(FACTOR(sum0, p) && FACTOR(sum8, 4))
w = (wrt[p << 4 | 4] = w) + hsum_off[isodd][p << 4 | 4];
for(p = 0; p < 9; p = (p + 1) * 2 % 11)
if(FACTOR(sum0, 4) && FACTOR(sum8, p))
w = (wrt[64 | p] = w) + hsum_off[isodd][64 | p];
for(y = 0; y < 1 << 20; ++y)
for(yy = 0; yy < 1 << 4; ++yy)
if(wrt[i = (p = hsum[yy << 16 | y >> 4]) << 4 | (r = hsum[y])]) {
*wrt[i] = (irr0 ? p == 4 : p) << 28 | (irr8 ? r == 4 : r) << 24;
*wrt[i]++ |= yy << 20 | y;
}
return *w = -1, set;
}
/** differential
* prune more states using filter flips and differential analysis
*/
uint32_t differential(uint32_t *list, uint32_t isodd, uint8_t byte,
uint8_t bbyte, uint16_t bsum8, uint32_t flip) {
uint32_t j, possible, k, invariant, i;
uint32_t y, yprime, lsb, jdiv;
uint32_t *read, *write, bit;
uint8_t *hsum = halfsum[isodd];
if(!flip && (bsum8 & 1)) return 0;
for(i = 0; i < 8 && BIT(byte, i) == BIT(bbyte, i); ++i);
k = (8 - i + !!isodd) >> 1;
for(write = read = list; *read != -1; ++read){
y = *read;
yprime = *read & ~((1 << k) - 1);
for(j = i, jdiv = k; j < 7 + !!isodd; ++j) {
invariant = BIT(byte, j) ^ BIT(bbyte, j);
invariant ^= BIT(y, 2 + jdiv) ^ BIT(yprime, 2 + jdiv);
invariant ^= filter(y >> jdiv) ^ filter(yprime >> jdiv);
if((j & 1) != !!isodd && invariant != 0) break;
j += (j & 1) != !!isodd;
jdiv--;
bit = BIT(y, jdiv);
bit ^= BIT(byte, j) ^ BIT(bbyte, j);
bit ^= BIT(y, 3 + jdiv) ^ BIT(yprime, 3 + jdiv);
bit ^= BIT(y, 4 + jdiv) ^ BIT(yprime, 4 + jdiv);
yprime |= bit << jdiv;
}
for(lsb = possible = 0; lsb < 1 << jdiv; ++lsb){
if(FACTOR(bsum8, hsum[0xfffff & (yprime | lsb)]))
if((flip & 1) == 0 || filterflip[0xfffff & (yprime | lsb)])
if((flip & 2) == 0 || filterflip[0xfffff & (yprime | lsb) >> 1])
if((flip & 4) == 0 || filterflip[0xfffff & (yprime | lsb) >> 2])
if((flip & 16) == 0 || !filterflip[0xfffff & (yprime | lsb)])
if((flip & 32) == 0 || !filterflip[0xfffff & (yprime | lsb) >> 1])
if((flip & 64) == 0 || !filterflip[0xfffff & (yprime | lsb) >> 2])
possible = 1;
}
if(possible) *write++ = y;
}
*write = -1;
return (uint32_t)(read - write);
}
/** binom
* calculate the binomial coefficient
*/
static double binom(uint32_t n, uint32_t k) {
double num = 1.0;
uint32_t i, t = (n - k > k) ? n - k : k;
if(k > n)
return 0;
for(i = t + 1; i <= n; ++i)
num *= i;
for(i = 2; i <= n - t; ++i)
num /= i;
return num;
}
/** predictsum
* passable prediction logic based on hypergeometric distribution
*/
static uint32_t predictsum(uint64_t *nonces, uint8_t byte, uint32_t *conf) {
uint32_t k, K, n, N = 256, bestK = 0, i;
uint8_t seen[256] = {0}, nonceb1, nonceb2;
double num, sum = 0.0, max = 0.0;
for(i = k = n = 0; nonces[i] != -1; ++i){
nonceb1 = nonces[i];
nonceb2 = nonces[i] >> 8;
if(nonceb1 == byte && !seen[nonceb2]) {
seen[nonceb2] = 1;
++n;
k += parity(nonceb2) ^ BIT(nonces[i], 40);
}
}
for(K = 0; K <= 256; K += 1) {
num = binom(K, k) * (binom(N - K, n - k) / binom(N, n));
sum += num * prob[K];
max = (num > max) ? bestK = K, num : max;
}
*conf = 100.0 * max * prob[bestK] / sum + 0.5;
return bestK;
}
/** getpredictions
* guess the sumproperty at time 8 for all possible first 8 bits
*/
uint32_t getpredictions(uint64_t *nonces, int tresh, uint32_t *pred) {
uint32_t i, none = 1, conf, sum8;
for(i = 0; i < 256; ++i){
sum8 = predictsum(nonces, i, &conf);
none &= pred[i] = (conf >= tresh) ? sum8 | conf << 16 : 129;
}
return !none;
}
/** bestb
* poor heuristic to find reasonable base for differential analysis
*/
uint8_t bestb(uint32_t *pred) {
uint32_t i, j, h, k;
uint32_t max = 0;
for(i = 0; i < 256; ++i) {
if(pred[i] & 1) continue;
for(j = 0, h = i; j < 256; ++j) {
if(i == j || (pred[j] & 1)) continue;
for(k = 0; k < 8 && BIT(i, k) == BIT(j, k); ++k);
h += k << 8;
}
max = (h > max) ? h : max;
}
return max;
}
/** findflips
* Detect some filter flip conditions
*/
uint32_t findflips(uint64_t *nonces, uint32_t *flips) {
uint32_t parities[256] = {0};
uint32_t i, status = 0;
for(i = 0; nonces[i] != -1; ++i)
parities[nonces[i] & 0xff] = BIT(nonces[i], 32);
for(i = 0; i < 0x100; ++i){
flips[i] = 0;
flips[i] |= (parities[i] == parities[i ^ 0x80]) << 0;
flips[i] |= (parities[i] == parities[i ^ 0x20]) << 1;
flips[i] |= (parities[i] == parities[i ^ 0x08]) << 2;
flips[i] |= (parities[i] == parities[i ^ 0x40]) << 8;
flips[i] |= (parities[i] == parities[i ^ 0x10]) << 9;
flips[i] |= (parities[i] == parities[i ^ 0x04]) << 10;
status |= flips[i];
}
for(i = 0; i < 0x30; ++i) {
flips[i] |= ((~flips[i] & 0x001) == 0x001) << 4;
flips[i] |= ((~flips[i] & 0x101) == 0x101) << 12;
flips[i] |= ((~flips[i] & 0x103) == 0x103) << 5;
flips[i] |= ((~flips[i] & 0x303) == 0x303) << 13;
flips[i] |= ((~flips[i] & 0x307) == 0x307) << 6;
flips[i] |= ((~flips[i] & 0x707) == 0x707) << 14;
}
for(i = 0; i < 0x100; ++i){
if(status & 1 << 0) flips[i] &= ~0x6066;
if(status & 1 << 1) flips[i] &= ~0x4044;
if(status & 1 << 8) flips[i] &= ~0x6640;
if(status & 1 << 9) flips[i] &= ~0x4400;
if((status & 7) == 7) flips[i] &= ~0x400;
}
return status;
}
static void __lfsr_rollback(uint64_t *s, uint32_t in) {
uint32_t bit, i;
uint64_t state = *s;
for(i = 0; i < 8; ++i) {
bit = state & 1;
state = state >> 32 | (state & 0xffffff) << 31;
bit ^= parity64(LF_POLY & state);
bit ^= in >> (7 - i);
bit ^= filter(state);
state |= (uint64_t)bit << 55;
}
*s = state;
}
static uint8_t inline paritycheck(uint64_t *s, uint32_t in) {
uint32_t feedin, i;
uint8_t ret = in >> 8;
for(i = 0; i < 8; ++i) {
ret ^= feedin = filter(*s);
feedin ^= parity64(LF_POLY & *s) ^ in >> i;
*s = *s << 32 | (uint32_t)(*s >> 31);
*s &= ~1ull;
*s |= feedin & 1;
}
return ret ^ filter(*s);
}
#define FOR_EACH_BYTE(X) (X) && (X) && (X) && (X)
uint64_t brute(uint32_t **task) {
uint32_t *oe = task[2], *p, i;
uint64_t *e, *eb, *ee, savestate, state, o, key;
eb = ee = malloc((1 << 20) + sizeof(uint64_t) * (task[4] - task[3]));
for(p = task[3]; p < task[4]; ++p) {
*ee = (uint64_t)*p << 32;
__lfsr_rollback(ee++, **task);
}
for(; task[1] < oe; ++task[1]) {
o = *task[1];
__lfsr_rollback(&o, 0);
for(e = eb; e < ee; ++e) {
state = savestate = o ^ *e;
i = 0;
p = task[0] + 10;
while(FOR_EACH_BYTE(!paritycheck(&state, *p++))) {
state = savestate;
if(++i == 100) goto out;
}
}
}
free(eb);
return -1;
out:
free(eb);
for(key = 0, i = 23; i < 24; --i)
key = key << 2 | BIT(state, i ^ 3) << 1 | BIT(state, 32 | (i ^ 3));
return key;
}
/** sumsplit
* Split sorted list of candidates into ranges. Based on msb.
*/
void sumsplit(uint32_t *list, uint32_t **ranges, uint32_t sum0, uint32_t sum8) {
uint32_t *last, p, i;
ranges[*list >> 24] = list;
for(last = list; *last != -1; ++last)
if(!ranges[*last >> 24]) {
ranges[*last >> 24] = last;
ranges[256 | *(last - 1) >> 24] = last;
}
ranges[256 | *(last - 1) >> 24] = last;
for(i = 0, p = 1; i < 16 && sum0 >> 1 == 64; i += p ^= 1)
ranges[p << 8 | 0x20 | i] = ranges[p << 8 | 0x10 | i];
for(i = 0; i < 32 && sum8 >> 1 == 64; ++i)
ranges[i << 4 | 2] = ranges[i << 4 | 1];
for(i = 0; i < 32 && (sum8 & 1); ++i)
ranges[i << 4 | 3] = ranges[i << 4];
}
/** mkspace
* split candidate lists into list of lists by matching halfsums
*/
uint32_t **mkspace(uint32_t *o, uint32_t *e, uint32_t sum0, uint32_t sum8) {
uint32_t *ohead[512] = {0}, **otail = ohead + 256, p, q, r, s;
uint32_t *ehead[512] = {0}, **etail = ehead + 256, **jobs, **j;
sumsplit(o, ohead, sum0, sum8);
sumsplit(e, ehead, sum0, sum8);
j = 1024 + (jobs = malloc(sizeof(uint32_t*) << 14));
*j++ = o;
*j++ = e;
for(p = 0; p != 4; p = (p + 1) * 2 % 11) {
for(r = 0; r != 4; r = (r + 1) * 2 % 11) {
q = (sum0 >> 1 == 64) ? !(p & 1) : DIVIDE(sum0, p);
s = (sum8 >> 1 == 64) ? !(r & 1) : DIVIDE(sum8, r);
if(q < 9 && s < 9 && ohead[p << 4 | r] && ehead[q << 4 | s]) {
*j++ = (uint32_t*)jobs;
*j++ = ohead[p << 4 | r];
*j++ = otail[p << 4 | r];
*j++ = ehead[q << 4 | s];
*j++ = etail[q << 4 | s];
}
}
}
return *j = 0, jobs;
}
/** craptev1_get_space
* Derive reduced search space from list of nested nonces.
* - returns a zero terminated list of partitions (5 pointers each)
* add 5 to the return value to get a pointer to the second partition.
* - uid is stored for use by search functions, it can be omitted.
*/
uint32_t** craptev1_get_space(uint64_t *nonces, uint32_t tresh, uint32_t uid) {
uint32_t sum0, sum8, pred[256], haspred, flips[256];
uint32_t *olist, *elist, i, **space, byte, *pre, b;
uint64_t t;
sum0 = getsum0(nonces);
if(sum0 == -1) return 0;
haspred = getpredictions(nonces, tresh, pred);
byte = haspred ? bestb(pred): 0xa5;
sum8 = pred[byte] & 0xffff;
findflips(nonces, flips);
olist = eliminate(sum0, sum8, 1);
elist = eliminate(sum0, sum8, 0);
for(i = 0; i < 256; ++i) {
differential(olist, 1, byte, i, pred[i], flips[i] & 255);
differential(elist, 0, byte, i, pred[i], flips[i] >> 8);
}
space = mkspace(olist, elist, sum0, sum8);
pre = (uint32_t*)space;
pre[0] = byte ^ uid >> 24;
pre[1] = uid;
for(i = 0, pre += 10; i < 400;)
for(b = 24, t = *nonces++; b < 32; b -= 8, t >>= 8, ++i)
pre[i] = parity((t ^ t >> 32) & 255) << 8 | ((t ^ uid >> b) & 255);
return space + 1026;
}
/** craptev1_sizeof_space
* Calculate the size of the search space
*/
uint64_t craptev1_sizeof_space(uint32_t **space) {
uint64_t i, c = 0, o, e;
for(i = 0; space[i]; i += 5) {
o = space[i + 2] - space[i + 1];
e = space[i + 4] - space[i + 3];
c += o * e;
}
return c;
}
/** craptev1_destroy_space
* Free all memory associated with a search space.
*/
void craptev1_destroy_space(uint32_t **space) {
free(*--space);
free(*--space);
free(space - 1024);
}
/** craptev1_search_partition
* Search one partition of the search space. Return key if found.
*/
uint64_t craptev1_search_partition(uint32_t **partition) {
return brute(partition);
}
/** craptev1_search_space
* Search entire search space.Return key if found.
*/
uint64_t craptev1_search_space(uint32_t **space) {
uint64_t i, key = -1;
for(i = 0; space[i] && key == -1; i += 5)
key = brute(space + i);
return key;
}

View File

@ -0,0 +1,49 @@
/**
* CraptEV1
* Copyright (c) 2015-2016 blapost@gmail.com
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted for non-commercial use only.
*
* No redistribution. No modifications.
*/
#ifndef CRAPTEV1_INCLUDED
#define CRAPTEV1_INCLUDED
#include <stdlib.h>
#include <inttypes.h>
#ifdef __cplusplus
extern "C" {
#endif
void craptev1_init();
uint32_t** craptev1_get_space(uint64_t *nonces, uint32_t tresh, uint32_t uid);
uint64_t craptev1_sizeof_space(uint32_t **space);
void craptev1_destroy_space(uint32_t **space);
uint64_t craptev1_search_partition(uint32_t **partition);
uint64_t craptev1_search_space(uint32_t **space);
#define parity(n) (__builtin_popcountl(n) & 1)
#define parity64(n) __builtin_popcountll(n)
#define BIT(x, n) ((x) >> (n) & 1)
static inline int filter(uint32_t const x) {
uint32_t f;
f = 0xf22c0 >> (x & 0xf) & 16;
f |= 0x6c9c0 >> (x >> 4 & 0xf) & 8;
f |= 0x3c8b0 >> (x >> 8 & 0xf) & 4;
f |= 0x1e458 >> (x >> 12 & 0xf) & 2;
f |= 0x0d938 >> (x >> 16 & 0xf) & 1;
return BIT(0xEC57E80A, f);
}
#ifdef __cplusplus
}
#endif
#endif

View File

@ -0,0 +1,19 @@
CRAPTEV1
--------
Partial implementation of attacks detailed in:
Ciphertext-only Cryptanalysis on Hardened Mifare Classic Cards
URL: http://www.cs.ru.nl/~rverdult/Ciphertext-only_Cryptanalysis_on_Hardened_Mifare_Classic_Cards-CCS_2015.pdf
Carlo Meijer(The Kerckhoffs Institute), Roel Verdult (Radboud University)
carlo@youcontent.nl, rverdult@cs.ru.nl
Authors of the paper are not authors of the code.
contents
--------
craptev1.c : main implementation library
craptev1.h : defines interface, and parity functions
solve.c : a demo linux x86_64 client
bla,
blapost@gmail.com

View File

@ -0,0 +1,122 @@
/**
* CraptEV1
* Copyright (c) 2015-2016 blapost@gmail.com
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted for non-commercial use only.
*
* No redistribution. No modifications.
*/
#define _GNU_SOURCE
#include "craptev1.h"
#include <stdio.h>
#include <unistd.h>
#include <sched.h>
#include <signal.h>
#include <sys/mman.h>
#include <linux/futex.h>
#include <sys/syscall.h>
#include <sys/sysinfo.h>
uint32_t **job;
uint64_t origsize;
void progress(int sig){
uint64_t left = craptev1_sizeof_space(job);
double p = (origsize - left) * 100.0 / origsize;
printf("\x1b[2K\x1b[G""%.2f%% done", p);
fflush(stdout);
alarm(1);
}
void progress_init(uint32_t **space){
origsize = craptev1_sizeof_space(job = space);
signal(SIGALRM, progress);
alarm(1);
}
int active;
int tmain(void *task){
uint64_t key = craptev1_search_partition(task);
if(key != -1) {
alarm(0);
printf("\nFOUND: %"PRIx64"\n", key);
exit(1);
}
__sync_sub_and_fetch(&active, 1);
syscall(__NR_futex, &active, FUTEX_WAKE, 1);
syscall(__NR_exit, 0);
return 0;
}
#define CLONE_FLAGS (CLONE_SIGHAND | CLONE_FS | CLONE_VM | CLONE_FILES | CLONE_THREAD | CLONE_SYSVSEM)
void multithread(uint32_t **space, int maxthread) {
char *stack;
int j;
for(j = 0; space[j * 5]; ++j) {
__sync_add_and_fetch(&active, 1);
stack = mmap(0, 4096, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, 0, 0);
clone(tmain, stack + 4092, CLONE_FLAGS, space + j * 5);
syscall(__NR_futex, &active, FUTEX_WAIT, maxthread, 0);
}
while((j = active))
syscall(__NR_futex, &active, FUTEX_WAIT, j, 0);
}
uint64_t *readnonces(char* fname) {
int i, j, r;
FILE *f = fopen(fname, "r");
uint64_t *nonces = malloc(sizeof (uint64_t) << 24);
uint32_t byte;
char parities;
for(i = 0; !feof(f); ++i) {
for(j = nonces[i] = 0; j < 4; ++j) {
r = fscanf(f, "%02x%c ", &byte, &parities);
if(r != 2) {
fprintf(stderr, "Input parse error pos:%ld\n", ftell(f));
fflush(stderr);
abort();
}
parities = (parities == '!') ^ parity(byte);
nonces[i] |= byte << 8 * j;
nonces[i] |= ((uint64_t)parities) << (32 + j * 8);
}
}
nonces[i] = -1;
fclose(f);
return nonces;
}
void usage(char *exename) {
printf("Usage:\n\t%s -f [filename] -u [uid] [-t treshold] [-n threads]\n\n", exename);
_exit(0);
}
int main(int argc, char**argv) {
uint64_t *nonces = 0, c;
uint32_t **space, uid = 0, tresh = 95;
int option, max_thread = get_nprocs_conf();
while((option = getopt(argc, argv, "f:u:n:t:")) != -1 )
switch(option) {
case 'f': nonces = readnonces(optarg); break;
case 'u': uid = strtoul(optarg, 0, 16); break;
case 'n': max_thread = atoi(optarg); break;
case 't': tresh = atoi(optarg); break;
default: usage(argv[0]);
}
if(optind != argc || nonces == 0)
usage(*argv);
space = craptev1_get_space(nonces, tresh, uid);
c = craptev1_sizeof_space(space);
printf("Leftover complexity: %"PRIx64"\n", c);
progress_init(space);
multithread(space, max_thread);
craptev1_destroy_space(space);
return 0;
}

479
crypto1_bs/crapto1-v3.3/crapto1.c Executable file
View File

@ -0,0 +1,479 @@
/* crapto1.c
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 2
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, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, US$
Copyright (C) 2008-2014 bla <blapost@gmail.com>
*/
#include "crapto1.h"
#include <stdlib.h>
#if !defined LOWMEM && defined __GNUC__
static uint8_t filterlut[1 << 20];
static void __attribute__((constructor)) fill_lut()
{
uint32_t i;
for(i = 0; i < 1 << 20; ++i)
filterlut[i] = filter(i);
}
#define filter(x) (filterlut[(x) & 0xfffff])
#endif
static void quicksort(uint32_t* const start, uint32_t* const stop)
{
uint32_t *it = start + 1, *rit = stop, t;
if(it > rit)
return;
while(it < rit)
if(*it <= *start)
++it;
else if(*rit > *start)
--rit;
else
t = *it, *it = *rit, *rit = t;
if(*rit >= *start)
--rit;
if(rit != start)
t = *rit, *rit = *start, *start = t;
quicksort(start, rit - 1);
quicksort(rit + 1, stop);
}
/** binsearch
* Binary search for the first occurence of *stop's MSB in sorted [start,stop]
*/
static inline uint32_t* binsearch(uint32_t *start, uint32_t *stop)
{
uint32_t mid, val = *stop & 0xff000000;
while(start != stop)
if(start[mid = (stop - start) >> 1] > val)
stop = &start[mid];
else
start += mid + 1;
return start;
}
/** update_contribution
* helper, calculates the partial linear feedback contributions and puts in MSB
*/
static inline void
update_contribution(uint32_t *item, const uint32_t mask1, const uint32_t mask2)
{
uint32_t p = *item >> 25;
p = p << 1 | parity(*item & mask1);
p = p << 1 | parity(*item & mask2);
*item = p << 24 | (*item & 0xffffff);
}
/** extend_table
* using a bit of the keystream extend the table of possible lfsr states
*/
static inline void
extend_table(uint32_t *tbl, uint32_t **end, int bit, int m1, int m2, uint32_t in)
{
in <<= 24;
for(*tbl <<= 1; tbl <= *end; *++tbl <<= 1)
if(filter(*tbl) ^ filter(*tbl | 1)) {
*tbl |= filter(*tbl) ^ bit;
update_contribution(tbl, m1, m2);
*tbl ^= in;
} else if(filter(*tbl) == bit) {
*++*end = tbl[1];
tbl[1] = tbl[0] | 1;
update_contribution(tbl, m1, m2);
*tbl++ ^= in;
update_contribution(tbl, m1, m2);
*tbl ^= in;
} else
*tbl-- = *(*end)--;
}
/** extend_table_simple
* using a bit of the keystream extend the table of possible lfsr states
*/
static inline void extend_table_simple(uint32_t *tbl, uint32_t **end, int bit)
{
for(*tbl <<= 1; tbl <= *end; *++tbl <<= 1)
if(filter(*tbl) ^ filter(*tbl | 1))
*tbl |= filter(*tbl) ^ bit;
else if(filter(*tbl) == bit) {
*++*end = *++tbl;
*tbl = tbl[-1] | 1;
} else
*tbl-- = *(*end)--;
}
/** recover
* recursively narrow down the search space, 4 bits of keystream at a time
*/
static struct Crypto1State*
recover(uint32_t *o_head, uint32_t *o_tail, uint32_t oks,
uint32_t *e_head, uint32_t *e_tail, uint32_t eks, int rem,
struct Crypto1State *sl, uint32_t in)
{
uint32_t *o, *e, i;
if(rem == -1) {
for(e = e_head; e <= e_tail; ++e) {
*e = *e << 1 ^ parity(*e & LF_POLY_EVEN) ^ !!(in & 4);
for(o = o_head; o <= o_tail; ++o, ++sl) {
sl->even = *o;
sl->odd = *e ^ parity(*o & LF_POLY_ODD);
sl[1].odd = sl[1].even = 0;
}
}
return sl;
}
for(i = 0; i < 4 && rem--; i++) {
oks >>= 1;
eks >>= 1;
in >>= 2;
extend_table(o_head, &o_tail, oks & 1, LF_POLY_EVEN << 1 | 1,
LF_POLY_ODD << 1, 0);
if(o_head > o_tail)
return sl;
extend_table(e_head, &e_tail, eks & 1, LF_POLY_ODD,
LF_POLY_EVEN << 1 | 1, in & 3);
if(e_head > e_tail)
return sl;
}
quicksort(o_head, o_tail);
quicksort(e_head, e_tail);
while(o_tail >= o_head && e_tail >= e_head)
if(((*o_tail ^ *e_tail) >> 24) == 0) {
o_tail = binsearch(o_head, o = o_tail);
e_tail = binsearch(e_head, e = e_tail);
sl = recover(o_tail--, o, oks,
e_tail--, e, eks, rem, sl, in);
}
else if(*o_tail > *e_tail)
o_tail = binsearch(o_head, o_tail) - 1;
else
e_tail = binsearch(e_head, e_tail) - 1;
return sl;
}
/** lfsr_recovery
* recover the state of the lfsr given 32 bits of the keystream
* additionally you can use the in parameter to specify the value
* that was fed into the lfsr at the time the keystream was generated
*/
struct Crypto1State* lfsr_recovery32(uint32_t ks2, uint32_t in)
{
struct Crypto1State *statelist;
uint32_t *odd_head = 0, *odd_tail = 0, oks = 0;
uint32_t *even_head = 0, *even_tail = 0, eks = 0;
int i;
for(i = 31; i >= 0; i -= 2)
oks = oks << 1 | BEBIT(ks2, i);
for(i = 30; i >= 0; i -= 2)
eks = eks << 1 | BEBIT(ks2, i);
odd_head = odd_tail = malloc(sizeof(uint32_t) << 21);
even_head = even_tail = malloc(sizeof(uint32_t) << 21);
statelist = malloc(sizeof(struct Crypto1State) << 18);
if(!odd_tail-- || !even_tail-- || !statelist) {
free(statelist);
statelist = 0;
goto out;
}
statelist->odd = statelist->even = 0;
for(i = 1 << 20; i >= 0; --i) {
if(filter(i) == (oks & 1))
*++odd_tail = i;
if(filter(i) == (eks & 1))
*++even_tail = i;
}
for(i = 0; i < 4; i++) {
extend_table_simple(odd_head, &odd_tail, (oks >>= 1) & 1);
extend_table_simple(even_head, &even_tail, (eks >>= 1) & 1);
}
in = (in >> 16 & 0xff) | (in << 16) | (in & 0xff00);
recover(odd_head, odd_tail, oks,
even_head, even_tail, eks, 11, statelist, in << 1);
out:
free(odd_head);
free(even_head);
return statelist;
}
static const uint32_t S1[] = { 0x62141, 0x310A0, 0x18850, 0x0C428, 0x06214,
0x0310A, 0x85E30, 0xC69AD, 0x634D6, 0xB5CDE, 0xDE8DA, 0x6F46D, 0xB3C83,
0x59E41, 0xA8995, 0xD027F, 0x6813F, 0x3409F, 0x9E6FA};
static const uint32_t S2[] = { 0x3A557B00, 0x5D2ABD80, 0x2E955EC0, 0x174AAF60,
0x0BA557B0, 0x05D2ABD8, 0x0449DE68, 0x048464B0, 0x42423258, 0x278192A8,
0x156042D0, 0x0AB02168, 0x43F89B30, 0x61FC4D98, 0x765EAD48, 0x7D8FDD20,
0x7EC7EE90, 0x7F63F748, 0x79117020};
static const uint32_t T1[] = {
0x4F37D, 0x279BE, 0x97A6A, 0x4BD35, 0x25E9A, 0x12F4D, 0x097A6, 0x80D66,
0xC4006, 0x62003, 0xB56B4, 0x5AB5A, 0xA9318, 0xD0F39, 0x6879C, 0xB057B,
0x582BD, 0x2C15E, 0x160AF, 0x8F6E2, 0xC3DC4, 0xE5857, 0x72C2B, 0x39615,
0x98DBF, 0xC806A, 0xE0680, 0x70340, 0x381A0, 0x98665, 0x4C332, 0xA272C};
static const uint32_t T2[] = { 0x3C88B810, 0x5E445C08, 0x2982A580, 0x14C152C0,
0x4A60A960, 0x253054B0, 0x52982A58, 0x2FEC9EA8, 0x1156C4D0, 0x08AB6268,
0x42F53AB0, 0x217A9D58, 0x161DC528, 0x0DAE6910, 0x46D73488, 0x25CB11C0,
0x52E588E0, 0x6972C470, 0x34B96238, 0x5CFC3A98, 0x28DE96C8, 0x12CFC0E0,
0x4967E070, 0x64B3F038, 0x74F97398, 0x7CDC3248, 0x38CE92A0, 0x1C674950,
0x0E33A4A8, 0x01B959D0, 0x40DCACE8, 0x26CEDDF0};
static const uint32_t C1[] = { 0x846B5, 0x4235A, 0x211AD};
static const uint32_t C2[] = { 0x1A822E0, 0x21A822E0, 0x21A822E0};
/** Reverse 64 bits of keystream into possible cipher states
* Variation mentioned in the paper. Somewhat optimized version
*/
struct Crypto1State* lfsr_recovery64(uint32_t ks2, uint32_t ks3)
{
struct Crypto1State *statelist, *sl;
uint8_t oks[32], eks[32], hi[32];
uint32_t low = 0, win = 0;
uint32_t *tail, table[1 << 16];
int i, j;
sl = statelist = malloc(sizeof(struct Crypto1State) << 4);
if(!sl)
return 0;
sl->odd = sl->even = 0;
for(i = 30; i >= 0; i -= 2) {
oks[i >> 1] = BEBIT(ks2, i);
oks[16 + (i >> 1)] = BEBIT(ks3, i);
}
for(i = 31; i >= 0; i -= 2) {
eks[i >> 1] = BEBIT(ks2, i);
eks[16 + (i >> 1)] = BEBIT(ks3, i);
}
for(i = 0xfffff; i >= 0; --i) {
if (filter(i) != oks[0])
continue;
*(tail = table) = i;
for(j = 1; tail >= table && j < 29; ++j)
extend_table_simple(table, &tail, oks[j]);
if(tail < table)
continue;
for(j = 0; j < 19; ++j)
low = low << 1 | parity(i & S1[j]);
for(j = 0; j < 32; ++j)
hi[j] = parity(i & T1[j]);
for(; tail >= table; --tail) {
for(j = 0; j < 3; ++j) {
*tail = *tail << 1;
*tail |= parity((i & C1[j]) ^ (*tail & C2[j]));
if(filter(*tail) != oks[29 + j])
goto continue2;
}
for(j = 0; j < 19; ++j)
win = win << 1 | parity(*tail & S2[j]);
win ^= low;
for(j = 0; j < 32; ++j) {
win = win << 1 ^ hi[j] ^ parity(*tail & T2[j]);
if(filter(win) != eks[j])
goto continue2;
}
*tail = *tail << 1 | parity(LF_POLY_EVEN & *tail);
sl->odd = *tail ^ parity(LF_POLY_ODD & win);
sl->even = win;
++sl;
sl->odd = sl->even = 0;
continue2:;
}
}
return statelist;
}
/** lfsr_rollback_bit
* Rollback the shift register in order to get previous states
*/
uint8_t lfsr_rollback_bit(struct Crypto1State *s, uint32_t in, int fb)
{
int out;
uint8_t ret;
uint32_t t;
s->odd &= 0xffffff;
t = s->odd, s->odd = s->even, s->even = t;
out = s->even & 1;
out ^= LF_POLY_EVEN & (s->even >>= 1);
out ^= LF_POLY_ODD & s->odd;
out ^= !!in;
out ^= (ret = filter(s->odd)) & !!fb;
s->even |= parity(out) << 23;
return ret;
}
/** lfsr_rollback_byte
* Rollback the shift register in order to get previous states
*/
uint8_t lfsr_rollback_byte(struct Crypto1State *s, uint32_t in, int fb)
{
int i, ret = 0;
for (i = 7; i >= 0; --i)
ret |= lfsr_rollback_bit(s, BIT(in, i), fb) << i;
return ret;
}
/** lfsr_rollback_word
* Rollback the shift register in order to get previous states
*/
uint32_t lfsr_rollback_word(struct Crypto1State *s, uint32_t in, int fb)
{
int i;
uint32_t ret = 0;
for (i = 31; i >= 0; --i)
ret |= lfsr_rollback_bit(s, BEBIT(in, i), fb) << (i ^ 24);
return ret;
}
/** nonce_distance
* x,y valid tag nonces, then prng_successor(x, nonce_distance(x, y)) = y
*/
static uint16_t *dist = 0;
int nonce_distance(uint32_t from, uint32_t to)
{
uint16_t x, i;
if(!dist) {
dist = malloc(2 << 16);
if(!dist)
return -1;
for (x = i = 1; i; ++i) {
dist[(x & 0xff) << 8 | x >> 8] = i;
x = x >> 1 | (x ^ x >> 2 ^ x >> 3 ^ x >> 5) << 15;
}
}
return (65535 + dist[to >> 16] - dist[from >> 16]) % 65535;
}
static uint32_t fastfwd[2][8] = {
{ 0, 0x4BC53, 0xECB1, 0x450E2, 0x25E29, 0x6E27A, 0x2B298, 0x60ECB},
{ 0, 0x1D962, 0x4BC53, 0x56531, 0xECB1, 0x135D3, 0x450E2, 0x58980}};
/** lfsr_prefix_ks
*
* Is an exported helper function from the common prefix attack
* Described in the "dark side" paper. It returns an -1 terminated array
* of possible partial(21 bit) secret state.
* The required keystream(ks) needs to contain the keystream that was used to
* encrypt the NACK which is observed when varying only the 3 last bits of Nr
* only correct iff [NR_3] ^ NR_3 does not depend on Nr_3
*/
uint32_t *lfsr_prefix_ks(uint8_t ks[8], int isodd)
{
uint32_t c, entry, *candidates = malloc(4 << 10);
int i, size = 0, good;
if(!candidates)
return 0;
for(i = 0; i < 1 << 21; ++i) {
for(c = 0, good = 1; good && c < 8; ++c) {
entry = i ^ fastfwd[isodd][c];
good &= (BIT(ks[c], isodd) == filter(entry >> 1));
good &= (BIT(ks[c], isodd + 2) == filter(entry));
}
if(good)
candidates[size++] = i;
}
candidates[size] = -1;
return candidates;
}
/** check_pfx_parity
* helper function which eliminates possible secret states using parity bits
*/
static struct Crypto1State*
check_pfx_parity(uint32_t prefix, uint32_t rresp, uint8_t parities[8][8],
uint32_t odd, uint32_t even, struct Crypto1State* sl)
{
uint32_t ks1, nr, ks2, rr, ks3, c, good = 1;
for(c = 0; good && c < 8; ++c) {
sl->odd = odd ^ fastfwd[1][c];
sl->even = even ^ fastfwd[0][c];
lfsr_rollback_bit(sl, 0, 0);
lfsr_rollback_bit(sl, 0, 0);
ks3 = lfsr_rollback_bit(sl, 0, 0);
ks2 = lfsr_rollback_word(sl, 0, 0);
ks1 = lfsr_rollback_word(sl, prefix | c << 5, 1);
nr = ks1 ^ (prefix | c << 5);
rr = ks2 ^ rresp;
good &= parity(nr & 0x000000ff) ^ parities[c][3] ^ BIT(ks2, 24);
good &= parity(rr & 0xff000000) ^ parities[c][4] ^ BIT(ks2, 16);
good &= parity(rr & 0x00ff0000) ^ parities[c][5] ^ BIT(ks2, 8);
good &= parity(rr & 0x0000ff00) ^ parities[c][6] ^ BIT(ks2, 0);
good &= parity(rr & 0x000000ff) ^ parities[c][7] ^ ks3;
}
return sl + good;
}
/** lfsr_common_prefix
* Implentation of the common prefix attack.
*/
struct Crypto1State*
lfsr_common_prefix(uint32_t pfx, uint32_t rr, uint8_t ks[8], uint8_t par[8][8])
{
struct Crypto1State *statelist, *s;
uint32_t *odd, *even, *o, *e, top;
odd = lfsr_prefix_ks(ks, 1);
even = lfsr_prefix_ks(ks, 0);
s = statelist = malloc((sizeof *statelist) << 20);
if(!s || !odd || !even) {
free(statelist);
statelist = 0;
goto out;
}
for(o = odd; *o + 1; ++o)
for(e = even; *e + 1; ++e)
for(top = 0; top < 64; ++top) {
*o += 1 << 21;
*e += (!(top & 7) + 1) << 21;
s = check_pfx_parity(pfx, rr, par, *o, *e, s);
}
s->odd = s->even = 0;
out:
free(odd);
free(even);
return statelist;
}

View File

@ -0,0 +1,93 @@
/* crapto1.h
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 2
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, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
MA 02110-1301, US$
Copyright (C) 2008-2014 bla <blapost@gmail.com>
*/
#ifndef CRAPTO1_INCLUDED
#define CRAPTO1_INCLUDED
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
struct Crypto1State {uint32_t odd, even;};
struct Crypto1State* crypto1_create(uint64_t);
void crypto1_destroy(struct Crypto1State*);
void crypto1_get_lfsr(struct Crypto1State*, uint64_t*);
uint8_t crypto1_bit(struct Crypto1State*, uint8_t, int);
uint8_t crypto1_byte(struct Crypto1State*, uint8_t, int);
uint32_t crypto1_word(struct Crypto1State*, uint32_t, int);
uint32_t prng_successor(uint32_t x, uint32_t n);
struct Crypto1State* lfsr_recovery32(uint32_t ks2, uint32_t in);
struct Crypto1State* lfsr_recovery64(uint32_t ks2, uint32_t ks3);
uint32_t *lfsr_prefix_ks(uint8_t ks[8], int isodd);
struct Crypto1State*
lfsr_common_prefix(uint32_t pfx, uint32_t rr, uint8_t ks[8], uint8_t par[8][8]);
uint8_t lfsr_rollback_bit(struct Crypto1State* s, uint32_t in, int fb);
uint8_t lfsr_rollback_byte(struct Crypto1State* s, uint32_t in, int fb);
uint32_t lfsr_rollback_word(struct Crypto1State* s, uint32_t in, int fb);
int nonce_distance(uint32_t from, uint32_t to);
#define FOREACH_VALID_NONCE(N, FILTER, FSIZE)\
uint32_t __n = 0,__M = 0, N = 0;\
int __i;\
for(; __n < 1 << 16; N = prng_successor(__M = ++__n, 16))\
for(__i = FSIZE - 1; __i >= 0; __i--)\
if(BIT(FILTER, __i) ^ parity(__M & 0xFF01))\
break;\
else if(__i)\
__M = prng_successor(__M, (__i == 7) ? 48 : 8);\
else
#define LF_POLY_ODD (0x29CE5C)
#define LF_POLY_EVEN (0x870804)
#define BIT(x, n) ((x) >> (n) & 1)
#define BEBIT(x, n) BIT(x, (n) ^ 24)
static inline int parity(uint32_t x)
{
#if !defined __i386__ || !defined __GNUC__
x ^= x >> 16;
x ^= x >> 8;
x ^= x >> 4;
return BIT(0x6996, x & 0xf);
#else
asm( "movl %1, %%eax\n"
"mov %%ax, %%cx\n"
"shrl $0x10, %%eax\n"
"xor %%ax, %%cx\n"
"xor %%ch, %%cl\n"
"setpo %%al\n"
"movzx %%al, %0\n": "=r"(x) : "r"(x): "eax","ecx");
return x;
#endif
}
static inline int filter(uint32_t const x)
{
uint32_t f;
f = 0xf22c0 >> (x & 0xf) & 16;
f |= 0x6c9c0 >> (x >> 4 & 0xf) & 8;
f |= 0x3c8b0 >> (x >> 8 & 0xf) & 4;
f |= 0x1e458 >> (x >> 12 & 0xf) & 2;
f |= 0x0d938 >> (x >> 16 & 0xf) & 1;
return BIT(0xEC57E80A, f);
}
#ifdef __cplusplus
}
#endif
#endif

View File

@ -0,0 +1,93 @@
/* crypto1.c
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 2
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, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
MA 02110-1301, US
Copyright (C) 2008-2008 bla <blapost@gmail.com>
*/
#include "crapto1.h"
#include <stdlib.h>
#define SWAPENDIAN(x)\
(x = (x >> 8 & 0xff00ff) | (x & 0xff00ff) << 8, x = x >> 16 | x << 16)
struct Crypto1State * crypto1_create(uint64_t key)
{
struct Crypto1State *s = malloc(sizeof(*s));
int i;
for(i = 47;s && i > 0; i -= 2) {
s->odd = s->odd << 1 | BIT(key, (i - 1) ^ 7);
s->even = s->even << 1 | BIT(key, i ^ 7);
}
return s;
}
void crypto1_destroy(struct Crypto1State *state)
{
free(state);
}
void crypto1_get_lfsr(struct Crypto1State *state, uint64_t *lfsr)
{
int i;
for(*lfsr = 0, i = 23; i >= 0; --i) {
*lfsr = *lfsr << 1 | BIT(state->odd, i ^ 3);
*lfsr = *lfsr << 1 | BIT(state->even, i ^ 3);
}
}
uint8_t crypto1_bit(struct Crypto1State *s, uint8_t in, int is_encrypted)
{
uint32_t feedin;
uint8_t ret = filter(s->odd);
feedin = ret & !!is_encrypted;
feedin ^= !!in;
feedin ^= LF_POLY_ODD & s->odd;
feedin ^= LF_POLY_EVEN & s->even;
s->even = s->even << 1 | parity(feedin);
s->odd ^= (s->odd ^= s->even, s->even ^= s->odd);
return ret;
}
uint8_t crypto1_byte(struct Crypto1State *s, uint8_t in, int is_encrypted)
{
uint8_t i, ret = 0;
for (i = 0; i < 8; ++i)
ret |= crypto1_bit(s, BIT(in, i), is_encrypted) << i;
return ret;
}
uint32_t crypto1_word(struct Crypto1State *s, uint32_t in, int is_encrypted)
{
uint32_t i, ret = 0;
for (i = 0; i < 32; ++i)
ret |= crypto1_bit(s, BEBIT(in, i), is_encrypted) << (i ^ 24);
return ret;
}
/* prng_successor
* helper used to obscure the keystream during authentication
*/
uint32_t prng_successor(uint32_t x, uint32_t n)
{
SWAPENDIAN(x);
while(n--)
x = x >> 1 | (x >> 16 ^ x >> 18 ^ x >> 19 ^ x >> 21) << 31;
return SWAPENDIAN(x);
}

27
crypto1_bs/crapto1-v3.3/readme Executable file
View File

@ -0,0 +1,27 @@
CRAPTO1
-------
Provides a set of library functions which aid the verification
of crypto1 weaknesses.
In short a partial implementation of:
Dismantling MIFARE Classic
URL: http://www.sos.cs.ru.nl/applications/rfid/2008-esorics.pdf
Flavio D. Garcia, Gerhard de Koning Gans, Ruben Muijrers,
Peter van Rossum, Roel Verdult, Ronny Wichers Schreur, Bart Jacobs
Institute for Computing and Information Sciences,
Radboud University Nijmegen, The Netherlands
{{flaviog,petervr,ronny,bart}@cs, {gkoningg,rmuijrer,rverdult}@sci}.ru.nl
and
Wirelessly Pickpocketing a Mifare Classic Card
URL: http://www.cs.ru.nl/~flaviog/publications/Pickpocketing.Mifare.pdf
Flavio D. Garcia, Peter van Rossum, Roel Verdult, Ronny Wichers Schreur
Radboud University Nijmegen, The Netherlands
{flaviog,petervr,rverdult,ronny}@cs.ru.nl
and
THE DARK SIDE OF SECURITY BY OBSCURITY
URL: http://eprint.iacr.org/2009/137
and Cloning MiFare Classic Rail and Building Passes, Anywhere, Anytime
Nicolas T. Courtois
University College London, Computer Science,
Gower street, WC1E 6BT, London, UK

121
crypto1_bs/crypto1_bs.c Normal file
View File

@ -0,0 +1,121 @@
// Bit-sliced Crypto-1 implementation
// The cipher states are stored with the least significant bit first, hence all bit indexes are reversed here
/*
Copyright (c) 2015-2016 Aram Verstegen
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
#include "crypto1_bs.h"
#include <inttypes.h>
#define __STDC_FORMAT_MACROS
#define llx PRIx64
#define lli PRIi64
#define lu PRIu32
// The following functions use this global or thread-local state
// It is sized to fit exactly KEYSTREAM_SIZE more states next to the initial state
__thread bitslice_t states[KEYSTREAM_SIZE+STATE_SIZE];
__thread bitslice_t * restrict state_p;
void crypto1_bs_init(){
// initialize constant one and zero bit vectors
memset(bs_ones.bytes, 0xff, VECTOR_SIZE);
memset(bs_zeroes.bytes, 0x00, VECTOR_SIZE);
}
// The following functions have side effects on 48 bitslices at the state_p pointer
// use the crypto1_bs_rewind_* macros to (re-)initialize them as needed
inline const bitslice_value_t crypto1_bs_bit(const bitslice_value_t input, const bool is_encrypted){
bitslice_value_t feedback = (state_p[47- 0].value ^ state_p[47- 5].value ^ state_p[47- 9].value ^
state_p[47-10].value ^ state_p[47-12].value ^ state_p[47-14].value ^
state_p[47-15].value ^ state_p[47-17].value ^ state_p[47-19].value ^
state_p[47-24].value ^ state_p[47-25].value ^ state_p[47-27].value ^
state_p[47-29].value ^ state_p[47-35].value ^ state_p[47-39].value ^
state_p[47-41].value ^ state_p[47-42].value ^ state_p[47-43].value);
const bitslice_value_t ks_bits = crypto1_bs_f20(state_p);
if(is_encrypted){
feedback ^= ks_bits;
}
state_p--;
state_p[0].value = feedback ^ input;
return ks_bits;
}
inline const bitslice_value_t crypto1_bs_lfsr_rollback(const bitslice_value_t input, const bool is_encrypted){
bitslice_value_t feedout = state_p[0].value;
state_p++;
const bitslice_value_t ks_bits = crypto1_bs_f20(state_p);
if(is_encrypted){
feedout ^= ks_bits;
}
const bitslice_value_t feedback = (feedout ^ state_p[47- 5].value ^ state_p[47- 9].value ^
state_p[47-10].value ^ state_p[47-12].value ^ state_p[47-14].value ^
state_p[47-15].value ^ state_p[47-17].value ^ state_p[47-19].value ^
state_p[47-24].value ^ state_p[47-25].value ^ state_p[47-27].value ^
state_p[47-29].value ^ state_p[47-35].value ^ state_p[47-39].value ^
state_p[47-41].value ^ state_p[47-42].value ^ state_p[47-43].value);
state_p[47].value = feedback ^ input;
return ks_bits;
}
// side-effect free from here on
// note that bytes are sliced and unsliced with reversed endianness
inline void crypto1_bs_convert_states(bitslice_t bitsliced_states[], state_t regular_states[]){
size_t bit_idx = 0, slice_idx = 0;
state_t values[MAX_BITSLICES];
memset(values, 0x0, sizeof(values));
for(slice_idx = 0; slice_idx < MAX_BITSLICES; slice_idx++){
for(bit_idx = 0; bit_idx < STATE_SIZE; bit_idx++){
bool bit = get_vector_bit(slice_idx, bitsliced_states[bit_idx]);
values[slice_idx].value <<= 1;
values[slice_idx].value |= bit;
}
// swap endianness
values[slice_idx].value = rev_state_t(values[slice_idx].value);
// roll off unused bits
values[slice_idx].value >>= ((sizeof(state_t)*8)-STATE_SIZE);
}
memcpy(regular_states, values, sizeof(values));
}
// bitslice a value
void crypto1_bs_bitslice_value32(uint32_t value, bitslice_t bitsliced_value[], size_t bit_len){
// load nonce bytes with unswapped endianness
size_t bit_idx;
for(bit_idx = 0; bit_idx < bit_len; bit_idx++){
bool bit = get_bit(bit_len-1-bit_idx, rev32(value));
if(bit){
bitsliced_value[bit_idx].value = bs_ones.value;
} else {
bitsliced_value[bit_idx].value = bs_zeroes.value;
}
}
}
void crypto1_bs_print_states(bitslice_t bitsliced_states[]){
size_t slice_idx = 0;
state_t values[MAX_BITSLICES];
crypto1_bs_convert_states(bitsliced_states, values);
for(slice_idx = 0; slice_idx < MAX_BITSLICES; slice_idx++){
printf("State %03zu: %012"llx"\n", slice_idx, values[slice_idx].value);
}
}

99
crypto1_bs/crypto1_bs.h Normal file
View File

@ -0,0 +1,99 @@
#ifndef _CRYPTO1_BS_H
#define _CRYPTO1_BS_H
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
// bitslice type
// while AVX supports 256 bit vector floating point operations, we need integer operations for boolean logic
// same for AVX2 and 512 bit vectors
// using larger vectors works but seems to generate more register pressure
#if defined(__AVX2__)
#define MAX_BITSLICES 256
#elif defined(__AVX__)
#define MAX_BITSLICES 128
#elif defined(__SSE2__)
#define MAX_BITSLICES 128
#else
#define MAX_BITSLICES 64
#endif
#define VECTOR_SIZE (MAX_BITSLICES/8)
typedef unsigned int __attribute__((aligned(VECTOR_SIZE))) __attribute__((vector_size(VECTOR_SIZE))) bitslice_value_t;
typedef union {
bitslice_value_t value;
uint64_t bytes64[MAX_BITSLICES/64];
uint8_t bytes[MAX_BITSLICES/8];
} bitslice_t;
// filter function (f20)
// sourced from ``Wirelessly Pickpocketing a Mifare Classic Card'' by Flavio Garcia, Peter van Rossum, Roel Verdult and Ronny Wichers Schreur
#define f20a(a,b,c,d) (((a|b)^(a&d))^(c&((a^b)|d)))
#define f20b(a,b,c,d) (((a&b)|c)^((a^b)&(c|d)))
#define f20c(a,b,c,d,e) ((a|((b|e)&(d^e)))^((a^(b&d))&((c^d)|(b&e))))
#define crypto1_bs_f20(s) \
f20c(f20a((s[47- 9].value), (s[47-11].value), (s[47-13].value), (s[47-15].value)), \
f20b((s[47-17].value), (s[47-19].value), (s[47-21].value), (s[47-23].value)), \
f20b((s[47-25].value), (s[47-27].value), (s[47-29].value), (s[47-31].value)), \
f20a((s[47-33].value), (s[47-35].value), (s[47-37].value), (s[47-39].value)), \
f20b((s[47-41].value), (s[47-43].value), (s[47-45].value), (s[47-47].value)))
// bit indexing
#define get_bit(n, word) ((word >> (n)) & 1)
#define get_vector_bit(slice, value) get_bit(slice&0x3f, value.bytes64[slice>>6])
// constant ones/zeroes
bitslice_t bs_ones;
bitslice_t bs_zeroes;
// size of crypto-1 state
#define STATE_SIZE 48
// size of nonce to be decrypted
#define KEYSTREAM_SIZE 32
// size of first uid^nonce byte to be rolled back to the initial key
#define ROLLBACK_SIZE 8
// number of nonces required to test to cover entire 48-bit state
// I would have said it's 12... but bla goes with 100, so I do too
#define NONCE_TESTS 100
// state pointer management
extern __thread bitslice_t states[KEYSTREAM_SIZE+STATE_SIZE];
extern __thread bitslice_t * restrict state_p;
// rewind to the point a0, at which KEYSTREAM_SIZE more bits can be generated
#define crypto1_bs_rewind_a0() (state_p = &states[KEYSTREAM_SIZE])
// bitsliced bytewise parity
#define bitsliced_byte_parity(n) (n[0].value ^ n[1].value ^ n[2].value ^ n[3].value ^ n[4].value ^ n[5].value ^ n[6].value ^ n[7].value)
// 48-bit crypto-1 states are normally represented using 64-bit values
typedef union {
uint64_t value;
uint8_t bytes[8];
} state_t;
// endianness conversion
#define rev32(word) (((word & 0xff) << 24) | (((word >> 8) & 0xff) << 16) | (((word >> 16) & 0xff) << 8) | (((word >> 24) & 0xff)))
#define rev64(x) (rev32(x)<<32|(rev32((x>>32))))
#define rev_state_t rev64
// crypto-1 functions
const bitslice_value_t crypto1_bs_bit(const bitslice_value_t input, const bool is_encrypted);
const bitslice_value_t crypto1_bs_lfsr_rollback(const bitslice_value_t input, const bool is_encrypted);
// initialization functions
void crypto1_bs_init();
// conversion functions
void crypto1_bs_bitslice_value32(uint32_t value, bitslice_t bitsliced_value[], size_t bit_len);
void crypto1_bs_convert_states(bitslice_t bitsliced_states[], state_t regular_states[]);
// debug print
void crypto1_bs_print_states(bitslice_t *bitsliced_states);
#endif // _CRYPTO1_BS_H

View File

@ -0,0 +1,256 @@
// Bit-sliced Crypto-1 brute-forcing implementation
// Builds on the data structures returned by CraptEV1 craptev1_get_space(nonces, threshold, uid)
/*
Copyright (c) 2015-2016 Aram Verstegen
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
#include <stdlib.h>
#ifndef __APPLE__
#include <malloc.h>
#endif
#include "crypto1_bs_crack.h"
inline uint64_t crack_states_bitsliced(uint32_t **task){
// the idea to roll back the half-states before combining them was suggested/explained to me by bla
// first we pre-bitslice all the even state bits and roll them back, then bitslice the odd bits and combine the two in the inner loop
uint64_t key = -1;
#ifdef EXACT_COUNT
size_t bucket_states_tested = 0;
size_t bucket_size[(task[4]-task[3])/MAX_BITSLICES];
#else
const size_t bucket_states_tested = (task[4]-task[3])*(task[2]-task[1]);
#endif
// bitslice all the even states
bitslice_t * restrict bitsliced_even_states[(task[4]-task[3])/MAX_BITSLICES];
size_t bitsliced_blocks = 0;
for(uint32_t const * restrict p_even = task[3]; p_even < task[4]; p_even+=MAX_BITSLICES){
#ifdef __WIN32
#ifdef __MINGW32__
bitslice_t * restrict lstate_p = __mingw_aligned_malloc((STATE_SIZE+ROLLBACK_SIZE) * sizeof(bitslice_t), sizeof(bitslice_t));
#else
bitslice_t * restrict lstate_p = _aligned_malloc((STATE_SIZE+ROLLBACK_SIZE) * sizeof(bitslice_t), sizeof(bitslice_t));
#endif
#else
#ifdef __APPLE__
bitslice_t * restrict lstate_p = malloc((STATE_SIZE+ROLLBACK_SIZE) * sizeof(bitslice_t));
#else
bitslice_t * restrict lstate_p = memalign(sizeof(bitslice_t), (STATE_SIZE+ROLLBACK_SIZE) * sizeof(bitslice_t));
#endif
#endif
memset(lstate_p, 0x0, (STATE_SIZE)*sizeof(bitslice_t));
// bitslice even half-states
const size_t max_slices = (task[4]-p_even) < MAX_BITSLICES ? task[4]-p_even : MAX_BITSLICES;
#ifdef EXACT_COUNT
bucket_size[bitsliced_blocks] = max_slices;
#endif
for(size_t slice_idx = 0; slice_idx < max_slices; ++slice_idx){
// set even bits
uint32_t e = *(p_even+slice_idx);
for(size_t bit_idx = 1; bit_idx < STATE_SIZE; bit_idx+=2, e >>= 1){
if(e&1){
lstate_p[bit_idx].bytes64[slice_idx>>6] |= 1ull << (slice_idx&63);
}
}
}
// compute the rollback bits
for(size_t rollback = 0; rollback < ROLLBACK_SIZE; ++rollback){
// inlined crypto1_bs_lfsr_rollback
const bitslice_value_t feedout = lstate_p[0].value;
++lstate_p;
const bitslice_value_t ks_bits = crypto1_bs_f20(lstate_p);
const bitslice_value_t feedback = (feedout ^ ks_bits ^ lstate_p[47- 5].value ^ lstate_p[47- 9].value ^
lstate_p[47-10].value ^ lstate_p[47-12].value ^ lstate_p[47-14].value ^
lstate_p[47-15].value ^ lstate_p[47-17].value ^ lstate_p[47-19].value ^
lstate_p[47-24].value ^ lstate_p[47-25].value ^ lstate_p[47-27].value ^
lstate_p[47-29].value ^ lstate_p[47-35].value ^ lstate_p[47-39].value ^
lstate_p[47-41].value ^ lstate_p[47-42].value ^ lstate_p[47-43].value);
lstate_p[47].value = feedback ^ bitsliced_rollback_byte[rollback].value;
}
bitsliced_even_states[bitsliced_blocks++] = lstate_p;
}
// bitslice every odd state to every block of even half-states with half-finished rollback
for(uint32_t const * restrict p_odd = task[1]; p_odd < task[2]; ++p_odd){
// early abort
if(keys_found){
goto out;
}
// set the odd bits and compute rollback
uint64_t o = (uint64_t) *p_odd;
lfsr_rollback_byte(&o, 0, 1);
// pre-compute part of the odd feedback bits (minus rollback)
bool odd_feedback_bit = parity(o&0x9ce5c);
crypto1_bs_rewind_a0();
// set odd bits
for(size_t state_idx = 0; state_idx < (STATE_SIZE-ROLLBACK_SIZE); o >>= 1, state_idx+=2){
if(o & 1){
state_p[state_idx] = bs_ones;
} else {
state_p[state_idx] = bs_zeroes;
}
}
const bitslice_value_t odd_feedback = odd_feedback_bit ? bs_ones.value : bs_zeroes.value;
// set even and rollback bits
for(size_t block_idx = 0; block_idx < bitsliced_blocks; ++block_idx){
const bitslice_t * const restrict bitsliced_even_state = bitsliced_even_states[block_idx];
size_t state_idx;
// set even bits
for(state_idx = 0; state_idx < (STATE_SIZE-ROLLBACK_SIZE); state_idx+=2){
state_p[1+state_idx] = bitsliced_even_state[1+state_idx];
}
// set rollback bits
uint64_t lo = o;
for(; state_idx < STATE_SIZE; lo >>= 1, state_idx+=2){
// set the odd bits and take in the odd rollback bits from the even states
if(lo & 1){
state_p[state_idx].value = ~bitsliced_even_state[state_idx].value;
} else {
state_p[state_idx] = bitsliced_even_state[state_idx];
}
// set the even bits and take in the even rollback bits from the odd states
if((lo >> 32) & 1){
state_p[1+state_idx].value = ~bitsliced_even_state[1+state_idx].value;
} else {
state_p[1+state_idx] = bitsliced_even_state[1+state_idx];
}
}
#ifdef EXACT_COUNT
// Fix a "1000000% bug". Looks like here is a problem with OS X gcc
size_t current_bucket_size = bucket_size[block_idx] > MAX_BITSLICES ? MAX_BITSLICES : bucket_size[block_idx];
bucket_states_tested += current_bucket_size;
#ifdef ONLINE_COUNT
__atomic_fetch_add(&total_states_tested, current_bucket_size, __ATOMIC_RELAXED);
#endif
#else
#ifdef ONLINE_COUNT
__atomic_fetch_add(&total_states_tested, MAX_BITSLICES, __ATOMIC_RELAXED);
#endif
#endif
// pre-compute first keystream and feedback bit vectors
const bitslice_value_t ksb = crypto1_bs_f20(state_p);
const bitslice_value_t fbb = (odd_feedback ^ state_p[47- 0].value ^ state_p[47- 5].value ^ // take in the even and rollback bits
state_p[47-10].value ^ state_p[47-12].value ^ state_p[47-14].value ^
state_p[47-24].value ^ state_p[47-42].value);
// vector to contain test results (1 = passed, 0 = failed)
bitslice_t results = bs_ones;
for(size_t tests = 0; tests < NONCE_TESTS; ++tests){
size_t parity_bit_idx = 0;
bitslice_value_t fb_bits = fbb;
bitslice_value_t ks_bits = ksb;
state_p = &states[KEYSTREAM_SIZE-1];
bitslice_value_t parity_bit_vector = bs_zeroes.value;
// highest bit is transmitted/received first
for(int32_t ks_idx = KEYSTREAM_SIZE-1; ks_idx >= 0; --ks_idx, --state_p){
// decrypt nonce bits
const bitslice_value_t encrypted_nonce_bit_vector = bitsliced_encrypted_nonces[tests][ks_idx].value;
const bitslice_value_t decrypted_nonce_bit_vector = (encrypted_nonce_bit_vector ^ ks_bits);
// compute real parity bits on the fly
parity_bit_vector ^= decrypted_nonce_bit_vector;
// update state
state_p[0].value = (fb_bits ^ decrypted_nonce_bit_vector);
// compute next keystream bit
ks_bits = crypto1_bs_f20(state_p);
// for each byte:
if((ks_idx&7) == 0){
// get encrypted parity bits
const bitslice_value_t encrypted_parity_bit_vector = bitsliced_encrypted_parity_bits[tests][parity_bit_idx++].value;
// decrypt parity bits
const bitslice_value_t decrypted_parity_bit_vector = (encrypted_parity_bit_vector ^ ks_bits);
// compare actual parity bits with decrypted parity bits and take count in results vector
results.value &= (parity_bit_vector ^ decrypted_parity_bit_vector);
// make sure we still have a match in our set
// if(memcmp(&results, &bs_zeroes, sizeof(bitslice_t)) == 0){
// this is much faster on my gcc, because somehow a memcmp needlessly spills/fills all the xmm registers to/from the stack - ???
// the short-circuiting also helps
if(results.bytes64[0] == 0
#if MAX_BITSLICES > 64
&& results.bytes64[1] == 0
#endif
#if MAX_BITSLICES > 128
&& results.bytes64[2] == 0
&& results.bytes64[3] == 0
#endif
){
goto stop_tests;
}
// this is about as fast but less portable (requires -std=gnu99)
// asm goto ("ptest %1, %0\n\t"
// "jz %l2" :: "xm" (results.value), "xm" (bs_ones.value) : "cc" : stop_tests);
parity_bit_vector = bs_zeroes.value;
}
// compute next feedback bit vector
fb_bits = (state_p[47- 0].value ^ state_p[47- 5].value ^ state_p[47- 9].value ^
state_p[47-10].value ^ state_p[47-12].value ^ state_p[47-14].value ^
state_p[47-15].value ^ state_p[47-17].value ^ state_p[47-19].value ^
state_p[47-24].value ^ state_p[47-25].value ^ state_p[47-27].value ^
state_p[47-29].value ^ state_p[47-35].value ^ state_p[47-39].value ^
state_p[47-41].value ^ state_p[47-42].value ^ state_p[47-43].value);
}
}
// all nonce tests were successful: we've found the key in this block!
state_t keys[MAX_BITSLICES];
crypto1_bs_convert_states(&states[KEYSTREAM_SIZE], keys);
for(size_t results_idx = 0; results_idx < MAX_BITSLICES; ++results_idx){
if(get_vector_bit(results_idx, results)){
key = keys[results_idx].value;
__atomic_fetch_add(&keys_found, 1, __ATOMIC_RELAXED);
goto out;
}
}
stop_tests:
// prepare to set new states
crypto1_bs_rewind_a0();
continue;
}
}
out:
for(size_t block_idx = 0; block_idx < bitsliced_blocks; ++block_idx){
#ifdef __WIN32
#ifdef __MINGW32__
__mingw_aligned_free(bitsliced_even_states[block_idx]-ROLLBACK_SIZE);
#else
_aligned_free(bitsliced_even_states[block_idx]-ROLLBACK_SIZE);
#endif
#else
free(bitsliced_even_states[block_idx]-ROLLBACK_SIZE);
#endif
}
#ifndef ONLINE_COUNT
__atomic_fetch_add(&total_states_tested, bucket_states_tested, __ATOMIC_RELAXED);
#endif
return key;
}

View File

@ -0,0 +1,22 @@
#ifndef _CRYPTO1_BS_CRACK_H
#define _CRYPTO1_BS_CRACK_H
#include <stdint.h>
#include "crypto1_bs.h"
#include "craptev1.h"
uint64_t crack_states_bitsliced(uint32_t **task);
size_t keys_found;
uint64_t total_states_tested;
uint64_t total_states;
// linked from crapto1.c file
extern uint8_t lfsr_rollback_byte(uint64_t* s, uint32_t in, int fb);
#define ONLINE_COUNT
#define EXACT_COUNT
// arrays of bitsliced states with identical values in all slices
bitslice_t bitsliced_encrypted_nonces[NONCE_TESTS][STATE_SIZE];
bitslice_t bitsliced_encrypted_parity_bits[NONCE_TESTS][STATE_SIZE];
bitslice_t bitsliced_rollback_byte[ROLLBACK_SIZE];
#endif // _CRYPTO1_BS_CRACK_H

661
crypto1_bs/holycard_collect.c Executable file
View File

@ -0,0 +1,661 @@
// Copyright (C) 2016 Aram Verstegen
/*
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users. This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it. (Some other Free Software Foundation software is covered by
the GNU Lesser General Public License instead.) You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have. You must make sure that they, too, receive or can get the
source code. And you must show them these terms so they know their
rights.
We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.
Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software. If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.
Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary. To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at all.
The precise terms and conditions for copying, distribution and
modification follow.
GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License. The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language. (Hereinafter, translation is included without limitation in
the term "modification".) Each licensee is addressed as "you".
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.
1. You may copy and distribute verbatim copies of the Program's
source code as you receive it, in any medium, provided that you
conspicuously and appropriately publish on each copy an appropriate
copyright notice and disclaimer of warranty; keep intact all the
notices that refer to this License and to the absence of any warranty;
and give any other recipients of the Program a copy of this License
along with the Program.
You may charge a fee for the physical act of transferring a copy, and
you may at your option offer warranty protection in exchange for a fee.
2. You may modify your copy or copies of the Program or any portion
of it, thus forming a work based on the Program, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) You must cause the modified files to carry prominent notices
stating that you changed the files and the date of any change.
b) You must cause any work that you distribute or publish, that in
whole or in part contains or is derived from the Program or any
part thereof, to be licensed as a whole at no charge to all third
parties under the terms of this License.
c) If the modified program normally reads commands interactively
when run, you must cause it, when started running for such
interactive use in the most ordinary way, to print or display an
announcement including an appropriate copyright notice and a
notice that there is no warranty (or else, saying that you provide
a warranty) and that users may redistribute the program under
these conditions, and telling the user how to view a copy of this
License. (Exception: if the Program itself is interactive but
does not normally print such an announcement, your work based on
the Program is not required to print an announcement.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Program, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Program.
In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:
a) Accompany it with the complete corresponding machine-readable
source code, which must be distributed under the terms of Sections
1 and 2 above on a medium customarily used for software interchange; or,
b) Accompany it with a written offer, valid for at least three
years, to give any third party, for a charge no more than your
cost of physically performing source distribution, a complete
machine-readable copy of the corresponding source code, to be
distributed under the terms of Sections 1 and 2 above on a medium
customarily used for software interchange; or,
c) Accompany it with the information you received as to the offer
to distribute corresponding source code. (This alternative is
allowed only for noncommercial distribution and only if you
received the program in object code or executable form with such
an offer, in accord with Subsection b above.)
The source code for a work means the preferred form of the work for
making modifications to it. For an executable work, complete source
code means all the source code for all modules it contains, plus any
associated interface definition files, plus the scripts used to
control compilation and installation of the executable. However, as a
special exception, the source code distributed need not include
anything that is normally distributed (in either source or binary
form) with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that component
itself accompanies the executable.
If distribution of executable or object code is made by offering
access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.
4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
void, and will automatically terminate your rights under this License.
However, parties who have received copies, or rights, from you under
this License will not have their licenses terminated so long as such
parties remain in full compliance.
5. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Program or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.
6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program subject to
these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.
7. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Program at all. For example, if a patent
license would not permit royalty-free redistribution of the Program by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Program.
If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply and the section as a whole is intended to apply in other
circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system, which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
may add an explicit geographical distribution limitation excluding
those countries, so that distribution is permitted only in or among
countries not thus excluded. In such case, this License incorporates
the limitation as if written in the body of this License.
9. The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the Program
specifies a version number of this License which applies to it and "any
later version", you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation. If the Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.
10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission. For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
make exceptions for this. Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.
NO WARRANTY
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.
END OF TERMS AND CONDITIONS
*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <signal.h>
#include <pthread.h>
#include <fcntl.h>
#include <unistd.h>
#include <nfc/nfc.h>
#include <math.h>
#include "crypto1_bs_crack.h"
#define llu PRIu64
extern uint64_t * crypto1_create(uint64_t key);
extern uint32_t crypto1_word(uint64_t *, uint32_t, int);
extern uint8_t crypto1_byte(uint64_t*, uint8_t, int);
extern uint32_t prng_successor(uint32_t x, uint32_t n);
extern void crypto1_destroy(uint64_t*);
#define MC_AUTH_A 0x60
#define MC_AUTH_B 0x61
nfc_device* pnd;
nfc_target target;
typedef uint8_t byte_t;
uint8_t oddparity(const uint8_t bt)
{
// cf http://graphics.stanford.edu/~seander/bithacks.html#ParityParallel
return (0x9669 >> ((bt ^(bt >> 4)) & 0xF)) & 1;
}
long long unsigned int bytes_to_num(uint8_t *src, uint32_t len)
{
uint64_t num = 0;
while (len--) {
num = (num << 8) | (*src);
src++;
}
return num;
}
// Sectors 0 to 31 have 4 blocks per sector.
// Sectors 32 to 39 have 16 blocks per sector.
uint8_t block_to_sector(uint8_t block)
{
uint8_t sector;
if(block < 128) {
return block >> 2;
}
block -= 128;
return 32 + (block >> 4);
}
static nfc_context *context;
#define MAX_FRAME_LEN 264
uint64_t *nonces = NULL;
size_t nonces_collected;
enum {
OK,
ERROR,
KEY_WRONG,
};
#define VT100_cleareol "\r\33[2K"
// Almost entirely based on code from Mifare Offline Cracker (MFOC) by Nethemba, cheers guys! :)
int nested_auth(uint32_t uid, uint64_t known_key, uint8_t ab_key, uint8_t for_block, uint8_t target_block, uint8_t target_key, FILE* fp)
{
uint64_t *pcs;
// Possible key counter, just continue with a previous "session"
uint8_t Nr[4] = { 0x00, 0x00, 0x00, 0x00 }; // Reader nonce
uint8_t Cmd[4] = { 0x00, 0x00, 0x00, 0x00 };
uint8_t ArEnc[8] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
uint8_t ArEncPar[8] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
uint8_t Rx[MAX_FRAME_LEN]; // Tag response
uint8_t RxPar[MAX_FRAME_LEN]; // Tag response
uint32_t Nt;
int i;
// Prepare AUTH command
Cmd[0] = ab_key;
Cmd[1] = for_block;
iso14443a_crc_append(Cmd, 2);
// We need full control over the CRC
if (nfc_device_set_property_bool(pnd, NP_HANDLE_CRC, false) < 0) {
nfc_perror(pnd, "nfc_device_set_property_bool crc");
return ERROR;
}
// Request plain tag-nonce
// TODO: Set NP_EASY_FRAMING option only once if possible
if (nfc_device_set_property_bool(pnd, NP_EASY_FRAMING, false) < 0) {
nfc_perror(pnd, "nfc_device_set_property_bool framing");
return ERROR;
}
if (nfc_initiator_transceive_bytes(pnd, Cmd, 4, Rx, sizeof(Rx), 0) < 0) {
fprintf(stdout, "Error while requesting plain tag-nonce ");
return ERROR;
}
if (nfc_device_set_property_bool(pnd, NP_EASY_FRAMING, true) < 0) {
nfc_perror(pnd, "nfc_device_set_property_bool");
return ERROR;
}
// Save the tag nonce (Nt)
Nt = bytes_to_num(Rx, 4);
// Init the cipher with key {0..47} bits
pcs = crypto1_create(known_key);
// Load (plain) uid^nt into the cipher {48..79} bits
crypto1_word(pcs, bytes_to_num(Rx, 4) ^ uid, 0);
// Generate (encrypted) nr+parity by loading it into the cipher
for (i = 0; i < 4; i++) {
// Load in, and encrypt the reader nonce (Nr)
ArEnc[i] = crypto1_byte(pcs, Nr[i], 0) ^ Nr[i];
ArEncPar[i] = filter(*pcs) ^ oddparity(Nr[i]);
}
// Skip 32 bits in the pseudo random generator
Nt = prng_successor(Nt, 32);
// Generate reader-answer from tag-nonce
for (i = 4; i < 8; i++) {
// Get the next random byte
Nt = prng_successor(Nt, 8);
// Encrypt the reader-answer (Nt' = suc2(Nt))
ArEnc[i] = crypto1_byte(pcs, 0x00, 0) ^(Nt & 0xff);
ArEncPar[i] = filter(*pcs) ^ oddparity(Nt);
}
// Finally we want to send arbitrary parity bits
if (nfc_device_set_property_bool(pnd, NP_HANDLE_PARITY, false) < 0) {
nfc_perror(pnd, "nfc_device_set_property_bool parity ");
return 1;
}
// Transmit reader-answer
int res;
if (((res = nfc_initiator_transceive_bits(pnd, ArEnc, 64, ArEncPar, Rx, sizeof(Rx), RxPar)) < 0) || (res != 32)) {
fprintf(stderr, "Reader-answer transfer error, exiting.. ");
return KEY_WRONG;
}
// Decrypt the tag answer and verify that suc3(Nt) is At
Nt = prng_successor(Nt, 32);
if (!((crypto1_word(pcs, 0x00, 0) ^ bytes_to_num(Rx, 4)) == (Nt & 0xFFFFFFFF))) {
fprintf(stderr, "[At] is not Suc3(Nt), something is wrong, exiting.. ");
return ERROR;
}
Cmd[0] = target_key;
Cmd[1] = target_block;
iso14443a_crc_append(Cmd, 2);
for (i = 0; i < 4; i++) {
ArEnc[i] = crypto1_byte(pcs, 0, 0) ^ Cmd[i];
ArEncPar[i] = filter(*pcs) ^ oddparity(Cmd[i]);
}
if (((res = nfc_initiator_transceive_bits(pnd, ArEnc, 32, ArEncPar, Rx, sizeof(Rx), RxPar)) < 0) || (res != 32)) {
fprintf(stderr, "Reader-answer transfer error, exiting.. ");
return ERROR;
}
if(fp){
for(i = 0; i < 4; i++){
fprintf(fp,"%02x", Rx[i]);
if(RxPar[i] != oddparity(Rx[i])){
fprintf(fp,"! ");
} else {
fprintf(fp," ");
}
}
fprintf(fp, "\n");
}
if(nonces){
nonces[nonces_collected] = 0;
for(i = 0; i < 4; i++){
nonces[nonces_collected] |= ((uint64_t) Rx[i]) << (8*i);
nonces[nonces_collected] |= ((uint64_t) !RxPar[i]) << (32 + (8*i));
}
nonces_collected++;
}
crypto1_destroy(pcs);
return OK;
}
uint32_t uid;
uint32_t **space;
uint64_t found_key;
size_t thread_count;
void* crack_states_thread(void* x){
const size_t thread_id = (size_t)x;
int j;
for(j = thread_id; space[j * 5]; j += thread_count) {
const uint64_t key = crack_states_bitsliced(space + j * 5);
if(key != -1){
found_key = key;
break;
}
}
return NULL;
}
bool stop_collection = false;
#define CUTOFF ((uint64_t) 1<<39)
void * update_predictions_thread(void* p){
while(!stop_collection){
if(nonces && uid){
if(space){
craptev1_destroy_space(space);
space = NULL;
}
space = craptev1_get_space(nonces, 95, uid);
}
if(space){
total_states = craptev1_sizeof_space(space);
}
sleep(1); // We don't need to check this more often than once per second
}
return NULL;
}
void notify_status_offline(int sig){
printf(VT100_cleareol "Cracking... %6.02f%%", (100.0*total_states_tested/(total_states)));
alarm(1);
fflush(stdout);
signal(SIGALRM, notify_status_offline);
}
void notify_status_online(int sig){
if(!total_states){
printf(VT100_cleareol "Collected %zu nonces... ", nonces_collected);
} else {
printf(VT100_cleareol "Collected %zu nonces... leftover complexity %"llu" (~2^%0.2f)", nonces_collected, total_states, log(total_states) / log(2));
alarm(0);
stop_collection = true;
return;
}
alarm(1);
fflush(stdout);
signal(SIGALRM, notify_status_online);
}
uint64_t known_key;
uint8_t for_block;
uint8_t ab_key;
uint8_t target_block;
uint8_t target_key;
FILE* fp;
const nfc_modulation nmMifare = {
.nmt = NMT_ISO14443A,
.nbr = NBR_106,
};
void * update_nonces_thread(void* v){
while(!stop_collection){
// Configure the CRC and Parity settings
nfc_device_set_property_bool(pnd,NP_HANDLE_CRC,true);
nfc_device_set_property_bool(pnd,NP_HANDLE_PARITY,true);
// Poll for a ISO14443A (MIFARE) tag
if (nfc_initiator_select_passive_target(pnd,nmMifare,NULL,0,&target)) {
nested_auth(uid, known_key, ab_key, for_block, target_block, target_key, fp);
} else {
printf(VT100_cleareol "Don't move the tag!");
fflush(stdout);
}
}
return NULL;
}
int main (int argc, const char * argv[]) {
nfc_init(&context);
pnd = nfc_open(context, NULL);
if (pnd == NULL) {
fprintf(stderr, "No NFC device connection\n");
return 1;
}
nfc_initiator_init(pnd);
nfc_device_set_property_bool(pnd,NP_ACTIVATE_FIELD,false);
// Let the reader only try once to find a tag
nfc_device_set_property_bool(pnd,NP_INFINITE_SELECT,false);
nfc_device_set_property_bool(pnd,NP_HANDLE_CRC,true);
nfc_device_set_property_bool(pnd,NP_HANDLE_PARITY,true);
nfc_device_set_property_bool(pnd,NP_AUTO_ISO14443_4, false);
uid = 0;
// Enable field so more power consuming cards can power themselves up
nfc_device_set_property_bool(pnd,NP_ACTIVATE_FIELD,true);
if (nfc_initiator_select_passive_target(pnd,nmMifare,NULL,0,&target)) {
uid = bytes_to_num(target.nti.nai.abtUid,target.nti.nai.szUidLen);
}
if(!uid){
fprintf(stderr, "No tag detected!\n");
// Disconnect from NFC device
nfc_close(pnd);
return 1;
}
if(argc < 6){
printf("%s <known key> <for block> <A|B> <target block> <A|B>\n", argv[0]);
nfc_close(pnd);
return 1;
}
known_key = strtoull(argv[1], 0, 16);
for_block = atoi(argv[2]);
ab_key = MC_AUTH_A;
if(argv[3][0] == 'b' || argv[3][0] == 'B'){
ab_key = MC_AUTH_B;
}
target_block = atoi(argv[4]);
target_key = MC_AUTH_A;
if(argv[5][0] == 'b' || argv[5][0] == 'B'){
target_key = MC_AUTH_B;
}
switch(nested_auth(uid, known_key, ab_key, for_block, target_block, target_key, NULL)){
case KEY_WRONG:
printf("%012"PRIx64" doesn't look like the right key %s for block %u (sector %u)\n", known_key, ab_key == MC_AUTH_A ? "A" : "B", for_block, block_to_sector(for_block));
return 1;
case OK:
break;
case ERROR:
default:
printf("Some other error occurred.\n");
break;
}
char filename[21];
sprintf(filename, "0x%08x_%03u%s.txt", uid, target_block, target_key == MC_AUTH_A ? "A" : "B");
fp = fopen(filename, "wb");
printf("Found tag with uid %04x, collecting nonces for key %s of block %u (sector %u) using known key %s %012"PRIx64" for block %u (sector %u)\n",
uid, target_key == MC_AUTH_A ? "A" : "B", target_block, block_to_sector(target_block), ab_key == MC_AUTH_A ? "A" : "B", known_key, for_block, block_to_sector(for_block));
nonces_collected = 0;
nonces = malloc(sizeof (uint64_t) << 24);
memset(nonces, 0xff, sizeof (uint64_t) << 24);
fcntl(0, F_SETFL, O_NONBLOCK);
signal(SIGALRM, notify_status_online);
alarm(1);
pthread_t prediction_thread, nonce_gathering_thread;
pthread_create(&nonce_gathering_thread, NULL, update_nonces_thread, NULL);
pthread_create(&prediction_thread, NULL, update_predictions_thread, NULL);
pthread_join(nonce_gathering_thread, 0);
pthread_join(prediction_thread, 0);
alarm(0);
if(fp){
fclose(fp);
}
nfc_close(pnd);
return 0;
}

145
crypto1_bs/holycard_solve.c Normal file
View File

@ -0,0 +1,145 @@
#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>
#include <unistd.h>
#include <signal.h>
#include <pthread.h>
#include "craptev1.h"
#include "crypto1_bs.h"
#include "crypto1_bs_crack.h"
#include <inttypes.h>
#include <math.h>
#define __STDC_FORMAT_MACROS
#define llx PRIx64
#define lli PRIi64
#define llu PRIu64
#define lu PRIu32
#define VT100_cleareol "\r\33[2K"
uint32_t **space;
uint8_t thread_count = 1;
uint64_t *readnonces(char* fname) {
int i, j;
FILE *f = fopen(fname, "r");
if (f == NULL) {
fprintf(stderr, "Cannot open file.\n");
exit(EXIT_FAILURE);
}
uint64_t *nonces = malloc(sizeof (uint64_t) << 24);
uint32_t nt;
char par;
i = 0;
while(!feof(f)){
nonces[i] = 0;
for(j = 0; j < 32; j += 8) {
if(2 != fscanf(f, "%02x%c ", &nt, &par)) {
fprintf(stderr, "Input format error at line:%d\n", i);
fflush(stderr);
exit(EXIT_FAILURE);
}
nonces[i] |= nt << j | (uint64_t)((par == '!') ^ parity(nt)) << (32 + j);
}
i++;
}
nonces[i] = -1;
fclose(f);
return nonces;
}
void* crack_states_thread(void* x){
const size_t thread_id = (size_t)x;
int j;
for(j = thread_id; space[j * 5]; j += thread_count) {
const uint64_t key = crack_states_bitsliced(space + j * 5);
if(key != -1){
printf("Found key: %012"llx"\n", key);
break;
} else if(keys_found){
break;
}
}
return NULL;
}
void notify_status_offline(int sig){
printf(VT100_cleareol "Cracking... %6.02f%%", (100.0*total_states_tested/(total_states)));
alarm(1);
fflush(stdout);
signal(SIGALRM, notify_status_offline);
}
int main(int argc, char* argv[]){
if(argc != 3){
printf("Usage: %s <nonces.txt> <uid>\n", argv[0]);
return -1;
}
printf("Reading nonces...\n");
uint64_t *nonces = readnonces(argv[1]);
uint32_t uid = strtoul(argv[2], NULL, 16);
printf("Deriving search space...\n");
space = craptev1_get_space(nonces, 95, uid);
total_states = craptev1_sizeof_space(space);
#ifndef __WIN32
thread_count = sysconf(_SC_NPROCESSORS_CONF);
#else
thread_count = 1;
#endif
// append some zeroes to the end of the space to make sure threads don't go off into the wild
size_t j = 0;
for(j = 0; space[j]; j+=5){
}
size_t fill = j + (5*thread_count);
for(; j < fill; j++) {
space[j] = 0;
}
pthread_t threads[thread_count];
size_t i;
printf("Initializing BS crypto-1\n");
crypto1_bs_init();
printf("Using %u-bit bitslices\n", MAX_BITSLICES);
uint8_t rollback_byte = **space;
printf("Bitslicing rollback byte: %02x...\n", rollback_byte);
// convert to 32 bit little-endian
crypto1_bs_bitslice_value32(rev32((rollback_byte)), bitsliced_rollback_byte, 8);
printf("Bitslicing nonces...\n");
for(size_t tests = 0; tests < NONCE_TESTS; tests++){
// pre-xor the uid into the decrypted nonces, and also pre-xor the uid parity into the encrypted parity bits - otherwise an exta xor is required in the decryption routine
uint32_t test_nonce = uid^rev32(nonces[tests]);
uint32_t test_parity = (nonces[tests]>>32)^rev32(uid);
test_parity = ((parity(test_parity >> 24 & 0xff) & 1) | (parity(test_parity>>16 & 0xff) & 1)<<1 | (parity(test_parity>>8 & 0xff) & 1)<<2 | (parity(test_parity & 0xff) & 1) << 3);
crypto1_bs_bitslice_value32(test_nonce, bitsliced_encrypted_nonces[tests], 32);
// convert to 32 bit little-endian
crypto1_bs_bitslice_value32(~(test_parity)<<24, bitsliced_encrypted_parity_bits[tests], 4);
}
total_states_tested = 0;
keys_found = 0;
printf("Starting %u threads to test %"llu" (~2^%0.2f) states\n", thread_count, total_states, log(total_states) / log(2));
signal(SIGALRM, notify_status_offline);
alarm(1);
for(i = 0; i < thread_count; i++){
pthread_create(&threads[i], NULL, crack_states_thread, (void*) i);
}
for(i = 0; i < thread_count; i++){
pthread_join(threads[i], 0);
}
alarm(0);
printf("\nTested %"llu" states\n", total_states_tested);
if(!keys_found) fprintf(stderr, "No solution found :(\n");
craptev1_destroy_space(space);
return 0;
}