Compare commits

..

No commits in common. "master" and "mfoc-0.10.4" have entirely different histories.

21 changed files with 243 additions and 1137 deletions

2
.gitignore vendored
View File

@ -8,7 +8,6 @@ config.h.in
config.log
config.status
configure
compile
depcomp
install-sh
missing
@ -18,6 +17,5 @@ src/.deps/
src/Makefile
src/Makefile.in
src/mfoc
src/mfoc.exe
stamp-h1

View File

@ -1,20 +0,0 @@
MFOC is an open source implementation of "offline nested" attack by Nethemba.
This program allow to recover authentication keys from MIFARE Classic card.
Please note MFOC is able to recover keys from target only if it have a known key: default one (hardcoded in MFOC) or custom one (user provided using command line).
# Build from source
```
autoreconf -is
./configure
make && sudo make install
```
# Usage #
Put one MIFARE Classic tag that you want keys recovering;
Lauching mfoc, you will need to pass options, see
```
mfoc -h
```

View File

@ -1,14 +1,14 @@
AC_INIT([mfoc],[0.10.7],[mifare@nethemba.com])
AC_INIT([mfoc],[0.10.4],[mifare@nethemba.com])
AC_CONFIG_MACRO_DIR([m4])
AC_PROG_CC
AC_CONFIG_HEADERS([config.h])
AC_CONFIG_SRCDIR([src/mfoc.c])
AM_INIT_AUTOMAKE(dist-bzip2 no-dist-gzip)
AC_PROG_CC
AM_INIT_AUTOMAKE
m4_ifdef([AM_SILENT_RULES],[AM_SILENT_RULES([yes])])

6
debian/.gitignore vendored Normal file
View File

@ -0,0 +1,6 @@
autoreconf.after
autoreconf.before
files
mfoc.debhelper.log
mfoc.substvars
mfoc/

68
debian/changelog vendored
View File

@ -1,67 +1,11 @@
mfoc (0.10.7+git20180724-1) unstable; urgency=medium
mfoc (0.10.4-0) unstable; urgency=low
* New upstream version 0.10.7+git20180724
* New upstream release
-- Samuel Henrique <samueloph@debian.org> Tue, 24 Jul 2018 01:19:50 -0300
-- Romuald Conty <romuald@libnfc.org> Sun, 20 Jan 2013 15:58:42 +0100
mfoc (0.10.7+git20150512-0kali1) kali; urgency=medium
mfoc (0.10.2pre3.1-0) unstable; urgency=low
* Import upstream (Closes: 0002240)
* Update debian files: watch, copyright
* Use debhelper 9
* Initial package
-- Sophie Brun <sophie@freexian.com> Tue, 12 May 2015 12:05:24 +0200
mfoc (0.10.7-0kali2) kali; urgency=low
* Updated watch file
-- Mati Aharoni <muts@kali.org> Sun, 12 Jan 2014 18:06:21 -0500
mfoc (0.10.7-0kali1) kali; urgency=low
* Upstream import
-- Mati Aharoni <muts@kali.org> Tue, 17 Dec 2013 09:12:38 -0500
mfoc (0.10.6-0kali0) kali; urgency=low
* Upstream import
-- Mati Aharoni <muts@kali.org> Mon, 19 Aug 2013 10:37:12 -0400
mfoc (0.10.5-0kali0) kali; urgency=low
* Upstream import.
-- Mati Aharoni <muts@kali.org> Sun, 24 Mar 2013 05:49:58 -0400
mfoc (0.10.3-1kali4) kali; urgency=low
* Removed desktop file
-- Mati Aharoni <muts@kali.org> Sat, 15 Dec 2012 14:23:37 -0500
mfoc (0.10.3-1kali3) kali; urgency=low
* Fixed compilation issue
-- Mati Aharoni <muts@kali.org> Tue, 04 Dec 2012 06:43:46 -0500
mfoc (0.10.3-1kali2) kali; urgency=low
* Version bump
-- Mati Aharoni <muts@kali.org> Sat, 01 Dec 2012 16:23:29 -0500
mfoc (0.10.3-1kali1) kali; urgency=low
* Version bump
-- Mati Aharoni <muts@kali.org> Sat, 01 Dec 2012 16:13:27 -0500
mfoc (0.10.3-1kali0) kali; urgency=low
* Initial release
-- Mati Aharoni <muts@kali.org> Sat, 01 Dec 2012 13:42:57 -0500
-- Thomas Hood <jdthood@gmail.com> Wed, 18 May 2011 12:00:00 +0200

2
debian/compat vendored
View File

@ -1 +1 @@
11
7

15
debian/control vendored
View File

@ -1,13 +1,12 @@
Source: mfoc
Section: utils
Priority: optional
Maintainer: Debian Security Tools <team+pkg-security@tracker.debian.org>
Uploaders: Samuel Henrique <samueloph@debian.org>
Build-Depends: debhelper (>= 11), libnfc-dev, pkg-config
Standards-Version: 4.1.5
Homepage: https://github.com/nfc-tools/mfoc
Vcs-Browser: https://salsa.debian.org/pkg-security-team/mfoc
Vcs-Git: https://salsa.debian.org/pkg-security-team/mfoc.git
Priority: extra
Maintainer: Thomas Hood <jdthood@gmail.com>
Build-Depends: debhelper (>= 7.0.50~), dh-autoreconf, libnfc-dev (>= 1.7.0), pkg-config
Standards-Version: 3.9.2
Homepage: http://code.google.com/p/nfc-tools/wiki/mfoc
Vcs-Svn: http://nfc-tools.googlecode.com/svn/trunk/mfoc
Vcs-Browser: http://code.google.com/p/nfc-tools/source/browse/#svn/trunk/mfoc
Package: mfoc
Architecture: any

75
debian/copyright vendored
View File

@ -1,59 +1,11 @@
Format: http://dep.debian.net/deps/dep5
Upstream-Name: MFOC
Source: https://github.com/nfc-tools/mfoc
Source: http://nfc-tools.googlecode.com/svn/trunk/mfoc
Files: *
Copyright: 2009 Norbert Szetei
2009 Pavol Luptak
2010 Micahal Boska
2010-2011 Romuald Conty <romuald@libnfc.org>
License: GPL-2+
Files: src/crypto1.c src/crapto1.c src/crapto1.h
Copyright: 2008-2009 bla <blapost@gmail.com>
License: GPL-2+
Files: src/slre.c src/slre.h
Copyright: 2013 Cesanta Software Limited
2004-2013 Sergey Lyubka <valenok@gmail.com>
License: GPL-2+
Files: src/nfc-utils.c src/mifare.c src/mifare.h src/nfc-utils.h
Copyright: 2010-2013 Philippe Teuwen
2009-2013 Romuald Conty <romuald@libnfc.org>
2009 Roel Verdult
2012-2013 Ludovic Rousseau <ludovic.rousseau@gmail.com>
2010-2012 Romain Tartière <romain.tartiere@gmail.com>
License: BSD-2-clause
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
.
1) Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
.
2 )Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
Files: debian/*
Copyright: 2011 Thomas Hood <jdthood@gmail.com>
2012-2014 Mati Aharoni <muts@kali.org>
2015 Sophie Brun <sophie@freexian.com
2018 Samuel Henrique <samueloph@debian.org>
License: GPL-2+
Copyright: 2009 Norbert Szetei, Pavol Luptak
2010 Micahal Boska, Romuald Conty
2011 Romuald Conty
License: GPL-2+
This package is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -71,3 +23,22 @@ License: GPL-2+
On Debian systems, the complete text of the GNU General Public
License version 2 can be found in "/usr/share/common-licenses/GPL-2".
Files: debian/*
Copyright: 2011 Thomas Hood <jdthood@gmail.com>
License: GPL-2+
This package 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 package 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/>
.
On Debian systems, the complete text of the GNU General Public
License version 2 can be found in "/usr/share/common-licenses/GPL-2".

14
debian/rules vendored
View File

@ -1,8 +1,16 @@
#!/usr/bin/make -f
export DEB_BUILD_MAINT_OPTIONS = hardening=+all
# -*- makefile -*-
# Sample debian/rules that uses debhelper.
# This file was originally written by Joey Hess and Craig Small.
# As a special exception, when this file is copied by dh-make into a
# dh-make output file, you may use that output file without restriction.
# This special exception was added by Craig Small in version 0.37 of dh-make.
%:
dh $@
# Uncomment this to turn on verbose mode.
#export DH_VERBOSE=1
override_dh_installchangelogs:
dh_installchangelogs ChangeLog
%:
dh --with autoreconf $@

8
debian/watch vendored
View File

@ -1,3 +1,7 @@
version=4
# See uscan(1) for format
# Compulsory line, this is a version 3 file
version=3
http://code.google.com/p/nfc-tools/downloads/list .*/mfoc-(.*).tar.gz
https://github.com/nfc-tools/mfoc/tags/ .*/mfoc-(.*)\.tar\.gz

View File

View File

@ -311,6 +311,7 @@ continue2:
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);
uint32_t *lfsr_prefix_ks(uint8_t ks[8], int isodd);
/** lfsr_rollback_bit
@ -374,12 +375,6 @@ int nonce_distance(uint32_t from, uint32_t to)
}
return (65535 + dist[to >> 16] - dist[from >> 16]) % 65535;
}
bool validate_prng_nonce(uint32_t nonce)
{
// init prng table:
nonce_distance(nonce, nonce);
return ((65535 - dist[nonce >> 16] + dist[nonce & 0xffff]) % 65535) == 16;
}
static uint32_t fastfwd[2][8] = {

View File

@ -20,7 +20,6 @@
#ifndef CRAPTO1_INCLUDED
#define CRAPTO1_INCLUDED
#include <stdint.h>
#include <stdbool.h>
#ifdef __cplusplus
extern "C" {
#endif
@ -40,7 +39,6 @@ extern "C" {
void lfsr_rollback(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);
bool validate_prng_nonce(uint32_t nonce);
#define FOREACH_VALID_NONCE(N, FILTER, FSIZE)\
uint32_t __n = 0,__M = 0, N = 0;\
int __i;\
@ -64,13 +62,13 @@ extern "C" {
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");
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
}

View File

@ -1,36 +1,34 @@
/*-
* Mifare Classic Offline Cracker
*
* 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, USA.
*
* Contact: <mifare@nethemba.com>
*
* Porting to libnfc 1.3.3: Michal Boska <boska.michal@gmail.com>
* Porting to libnfc 1.3.9 and upper: Romuald Conty <romuald@libnfc.org>
*
*/
/*
* This implementation was written based on information provided by the
* following documents:
*
* http://eprint.iacr.org/2009/137.pdf
* http://www.sos.cs.ru.nl/applications/rfid/2008-esorics.pdf
* http://www.cosic.esat.kuleuven.be/rfidsec09/Papers/mifare_courtois_rfidsec09.pdf
* http://www.cs.ru.nl/~petervr/papers/grvw_2009_pickpocket.pdf
*/
Mifare Classic Offline Cracker
Requirements: crapto1 library http://code.google.com/p/crapto1
libnfc http://www.libnfc.org
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, see <http://www.gnu.org/licenses/>.
Contact: <mifare@nethemba.com>
Porting to libnfc 1.3.3: Michal Boska <boska.michal@gmail.com>
Porting to libnfc 1.3.9: Romuald Conty <romuald@libnfc.org>
Porting to libnfc 1.4.x: Romuald Conty <romuald@libnfc.org>
URL http://eprint.iacr.org/2009/137.pdf
URL http://www.sos.cs.ru.nl/applications/rfid/2008-esorics.pdf
URL http://www.cosic.esat.kuleuven.be/rfidsec09/Papers/mifare_courtois_rfidsec09.pdf
URL http://www.cs.ru.nl/~petervr/papers/grvw_2009_pickpocket.pdf
*/
#define _XOPEN_SOURCE 1 // To enable getopt
@ -52,28 +50,15 @@
#include "nfc-utils.h"
#include "mfoc.h"
//SLRE
#include "slre.h"
#include "slre.c"
#define MAX_FRAME_LEN 264
static const nfc_modulation nm = {
.nmt = NMT_ISO14443A,
.nbr = NBR_106,
};
nfc_context *context;
uint64_t knownKey = 0;
char knownKeyLetter = 'A';
uint32_t knownSector = 0;
uint32_t unknownSector = 0;
char unknownKeyLetter = 'A';
uint32_t unexpected_random = 0;
int main(int argc, char *const argv[])
{
const nfc_modulation nm = {
.nmt = NMT_ISO14443A,
.nbr = NBR_106,
};
int ch, i, k, n, j, m;
int key, block;
int succeed = 1;
@ -110,36 +95,25 @@ int main(int argc, char *const argv[])
};
mftag t;
mfreader r;
denonce d = {NULL, 0, DEFAULT_DIST_NR, DEFAULT_TOLERANCE, {0x00, 0x00, 0x00}};
mftag t;
mfreader r;
denonce d = {NULL, 0, DEFAULT_DIST_NR, DEFAULT_TOLERANCE, {0x00, 0x00, 0x00}};
// Pointers to possible keys
pKeys *pk;
countKeys *ck;
pKeys *pk;
countKeys *ck;
// Pointer to already broken keys, except defaults
bKeys *bk;
bKeys *bk;
static mifare_param mp, mtmp;
static mifare_param mp;
static mifare_classic_tag mtDump;
mifare_cmd mc;
FILE *pfDump = NULL;
FILE *pfKey = NULL;
//File pointers for the keyfile
FILE * fp;
char line[20];
size_t len = 0;
char * read;
//Regexp declarations
static const char *regex = "([0-9A-Fa-f][0-9A-Fa-f][0-9A-Fa-f][0-9A-Fa-f][0-9A-Fa-f][0-9A-Fa-f][0-9A-Fa-f][0-9A-Fa-f][0-9A-Fa-f][0-9A-Fa-f][0-9A-Fa-f][0-9A-Fa-f])";
struct slre_cap caps[2];
// Parse command line arguments
while ((ch = getopt(argc, argv, "hD:s:BP:T:S:O:k:t:f:")) != -1) {
while ((ch = getopt(argc, argv, "hD:s:BP:T:S:O:k:t:")) != -1) {
switch (ch) {
case 'P':
// Number of probes
@ -160,31 +134,6 @@ int main(int argc, char *const argv[])
// fprintf(stdout, "Tolerance number: %d\n", probes);
}
break;
case 'f':
if (!(fp = fopen(optarg, "r"))) {
fprintf(stderr, "Cannot open keyfile: %s, exiting\n", optarg);
exit(EXIT_FAILURE);
}
while ((read = fgets(line, sizeof(line), fp)) != NULL) {
int i, j = 0, str_len = strlen(line);
while (j < str_len &&
(i = slre_match(regex, line + j, str_len - j, caps, 500, 1)) > 0) {
//We've found a key, let's add it to the structure.
p = realloc(defKeys, defKeys_len + 6);
if (!p) {
ERR("Cannot allocate memory for defKeys");
exit(EXIT_FAILURE);
}
defKeys = p;
memset(defKeys + defKeys_len, 0, 6);
num_to_bytes(strtoll(caps[0].ptr, NULL, 16), 6, defKeys + defKeys_len);
fprintf(stdout, "The custom key 0x%.*s has been added to the default keys\n", caps[0].len, caps[0].ptr);
defKeys_len = defKeys_len + 6;
j += i;
}
}
break;
case 'k':
// Add this key to the default keys
p = realloc(defKeys, defKeys_len + 6);
@ -207,14 +156,6 @@ int main(int argc, char *const argv[])
}
// fprintf(stdout, "Output file: %s\n", optarg);
break;
case 'D':
// Partial File output
if (!(pfKey = fopen(optarg, "w"))) {
fprintf(stderr, "Cannot open: %s, exiting\n", optarg);
exit(EXIT_FAILURE);
}
// fprintf(stdout, "Output file: %s\n", optarg);
break;
case 'h':
usage(stdout, 0);
break;
@ -257,59 +198,28 @@ int main(int argc, char *const argv[])
}
/*
// wait for tag to appear
for (i=0;!nfc_initiator_select_passive_target(r.pdi, nm, NULL, 0, &t.nt) && i < 10; i++) zsleep (100);
// wait for tag to appear
for (i=0;!nfc_initiator_select_passive_target(r.pdi, nm, NULL, 0, &t.nt) && i < 10; i++) zsleep (100);
*/
int tag_count;
if ((tag_count = nfc_initiator_select_passive_target(r.pdi, nm, NULL, 0, &t.nt)) < 0) {
// mf_select_tag(r.pdi, &(t.nt));
if (nfc_initiator_select_passive_target(r.pdi, nm, NULL, 0, &t.nt) < 0) {
nfc_perror(r.pdi, "nfc_initiator_select_passive_target");
goto error;
} else if (tag_count == 0) {
ERR("No tag found.");
goto error;
}
// Test if a compatible MIFARE tag is used
if (((t.nt.nti.nai.btSak & 0x08) == 0) && (t.nt.nti.nai.btSak != 0x01)) {
if ((t.nt.nti.nai.btSak & 0x08) == 0) {
ERR("only Mifare Classic is supported");
goto error;
}
// Save tag's block size (b4K)
t.b4K = (t.nt.nti.nai.abtAtqa[1] == 0x02);
t.authuid = (uint32_t) bytes_to_num(t.nt.nti.nai.abtUid + t.nt.nti.nai.szUidLen - 4, 4);
// Get Mifare Classic type from SAK
// see http://www.nxp.com/documents/application_note/AN10833.pdf Section 3.2
switch (t.nt.nti.nai.btSak)
{
case 0x01:
case 0x08:
case 0x88:
case 0x19:
if (get_rats_is_2k(t, r)) {
printf("Found Mifare Plus 2k tag\n");
t.num_sectors = NR_TRAILERS_2k;
t.num_blocks = NR_BLOCKS_2k;
} else {
printf("Found Mifare Classic 1k tag\n");
t.num_sectors = NR_TRAILERS_1k;
t.num_blocks = NR_BLOCKS_1k;
}
break;
case 0x09:
printf("Found Mifare Classic Mini tag\n");
t.num_sectors = NR_TRAILERS_MINI;
t.num_blocks = NR_BLOCKS_MINI;
break;
case 0x18:
printf("Found Mifare Classic 4k tag\n");
t.num_sectors = NR_TRAILERS_4k;
t.num_blocks = NR_BLOCKS_4k;
break;
default:
ERR("Cannot determine card type from SAK");
goto error;
}
t.num_blocks = (t.b4K) ? 0xff : 0x3f;
t.num_sectors = t.b4K ? NR_TRAILERS_4k : NR_TRAILERS_1k;
t.sectors = (void *) calloc(t.num_sectors, sizeof(sector));
if (t.sectors == NULL) {
@ -339,17 +249,16 @@ int main(int argc, char *const argv[])
t.sectors[s].foundKeyA = t.sectors[s].foundKeyB = false;
}
print_nfc_target(&t.nt, true);
print_nfc_target(t.nt, true);
fprintf(stdout, "\nTry to authenticate to all sectors with default keys...\n");
fprintf(stdout, "Symbols: '.' no key found, '/' A key found, '\\' B key found, 'x' both keys found\n");
// Try to authenticate to all sectors with default keys
// Set the authentication information (uid)
memcpy(mp.mpa.abtAuthUid, t.nt.nti.nai.abtUid + t.nt.nti.nai.szUidLen - 4, sizeof(mp.mpa.abtAuthUid));
// Iterate over all keys (n = number of keys)
n = sizeof(defaultKeys) / sizeof(defaultKeys[0]);
size_t defKey_bytes_todo = defKeys_len;
key = 0;
while (key < n || defKey_bytes_todo) {
while (key < n) {
if (defKey_bytes_todo > 0) {
memcpy(mp.mpa.abtKey, defKeys + defKeys_len - defKey_bytes_todo, sizeof(mp.mpa.abtKey));
defKey_bytes_todo -= sizeof(mp.mpa.abtKey);
@ -365,58 +274,21 @@ int main(int argc, char *const argv[])
if (trailer_block(block)) {
if (!t.sectors[i].foundKeyA) {
mc = MC_AUTH_A;
int res;
if ((res = nfc_initiator_mifare_cmd(r.pdi, mc, block, &mp)) < 0) {
if (res != NFC_EMFCAUTHFAIL) {
nfc_perror(r.pdi, "nfc_initiator_mifare_cmd");
goto error;
}
if (!nfc_initiator_mifare_cmd(r.pdi, mc, block, &mp)) {
// fprintf(stdout, "!!Error: AUTH [Key A:%012llx] sector %02x t_block %02x\n",
// bytes_to_num(mp.mpa.abtKey, 6), i, block);
mf_anticollision(t, r);
} else {
// Save all information about successfull keyA authentization
memcpy(t.sectors[i].KeyA, mp.mpa.abtKey, sizeof(mp.mpa.abtKey));
t.sectors[i].foundKeyA = true;
// Although KeyA can never be directly read from the data sector, KeyB can, so
// if we need KeyB for this sector, it should be revealed by a data read with KeyA
// todo - check for duplicates in cracked key list (do we care? will not be huge overhead)
// todo - make code more modular! :)
if (!t.sectors[i].foundKeyB) {
if ((res = nfc_initiator_mifare_cmd(r.pdi, MC_READ, block, &mtmp)) >= 0) {
// print only for debugging as it messes up output!
//fprintf(stdout, " Data read with Key A revealed Key B: [%012llx] - checking Auth: ", bytes_to_num(mtmp.mpd.abtData + 10, sizeof(mtmp.mpa.abtKey)));
memcpy(mtmp.mpa.abtKey, mtmp.mpd.abtData + 10, sizeof(mtmp.mpa.abtKey));
memcpy(mtmp.mpa.abtAuthUid, t.nt.nti.nai.abtUid + t.nt.nti.nai.szUidLen - 4, sizeof(mtmp.mpa.abtAuthUid));
if ((res = nfc_initiator_mifare_cmd(r.pdi, MC_AUTH_B, block, &mtmp)) < 0) {
//fprintf(stdout, "Failed!\n");
mf_configure(r.pdi);
mf_anticollision(t, r);
} else {
//fprintf(stdout, "OK\n");
memcpy(t.sectors[i].KeyB, mtmp.mpd.abtData + 10, sizeof(t.sectors[i].KeyB));
t.sectors[i].foundKeyB = true;
bk->size++;
bk->brokenKeys = (uint64_t *) realloc((void *)bk->brokenKeys, bk->size * sizeof(uint64_t));
bk->brokenKeys[bk->size - 1] = bytes_to_num(mtmp.mpa.abtKey, sizeof(mtmp.mpa.abtKey));
}
} else {
if (res != NFC_ERFTRANS) {
nfc_perror(r.pdi, "nfc_initiator_mifare_cmd");
goto error;
}
mf_anticollision(t, r);
}
}
}
}
// if key reveal failed, try other keys
if (!t.sectors[i].foundKeyB) {
mc = MC_AUTH_B;
int res;
if ((res = nfc_initiator_mifare_cmd(r.pdi, mc, block, &mp)) < 0) {
if (res != NFC_EMFCAUTHFAIL) {
nfc_perror(r.pdi, "nfc_initiator_mifare_cmd");
goto error;
}
if (!nfc_initiator_mifare_cmd(r.pdi, mc, block, &mp)) {
// fprintf(stdout, "!!Error: AUTH [Key B:%012llx] sector %02x t_block %02x\n",
// bytes_to_num(mp.mpa.abtKey, 6), i, block);
mf_anticollision(t, r);
// No success, try next block
t.sectors[i].trailer = block;
@ -435,6 +307,8 @@ int main(int argc, char *const argv[])
fprintf(stdout, ".");
}
fflush(stdout);
// fprintf(stdout, "\nSuccess: AUTH [Key %c:%012llx] sector %02x t_block %02x\n",
// (mc == MC_AUTH_A ? 'A' :'B'), bytes_to_num(mp.mpa.abtKey, 6), i, block);
// Save position of a trailer block to sector struct
t.sectors[i++].trailer = block;
}
@ -444,28 +318,9 @@ int main(int argc, char *const argv[])
fprintf(stdout, "\n");
for (i = 0; i < (t.num_sectors); ++i) {
if(t.sectors[i].foundKeyA){
fprintf(stdout, "Sector %02d - Found Key A: %012llx ", i, bytes_to_num(t.sectors[i].KeyA, sizeof(t.sectors[i].KeyA)));
memcpy(&knownKey, t.sectors[i].KeyA, 6);
knownKeyLetter = 'A';
knownSector = i;
}
else{
fprintf(stdout, "Sector %02d - Unknown Key A ", i);
unknownSector = i;
unknownKeyLetter = 'A';
}
if(t.sectors[i].foundKeyB){
fprintf(stdout, "Found Key B: %012llx\n", bytes_to_num(t.sectors[i].KeyB, sizeof(t.sectors[i].KeyB)));
knownKeyLetter = 'B';
memcpy(&knownKey, t.sectors[i].KeyB, 6);
knownSector = i;
}
else{
fprintf(stdout, "Unknown Key B\n");
unknownSector = i;
unknownKeyLetter = 'B';
}
fprintf(stdout, "Sector %02d - %12s ", i, ((t.sectors[i].foundKeyA) ? " FOUND_KEY [A]" : " UNKNOWN_KEY [A]"));
fprintf(stdout, "Sector %02d - %12s ", i, ((t.sectors[i].foundKeyB) ? " FOUND_KEY [B]" : " UNKNOWN_KEY [B]"));
fprintf(stdout, "\n");
}
fflush(stdout);
@ -484,51 +339,22 @@ int main(int argc, char *const argv[])
skip = false;
for (uint32_t o = 0; o < bk->size; o++) {
num_to_bytes(bk->brokenKeys[o], 6, mp.mpa.abtKey);
mc = dumpKeysA ? MC_AUTH_A : MC_AUTH_B;
int res;
if ((res = nfc_initiator_mifare_cmd(r.pdi, mc, t.sectors[j].trailer, &mp)) < 0) {
if (res != NFC_EMFCAUTHFAIL) {
nfc_perror(r.pdi, "nfc_initiator_mifare_cmd");
goto error;
}
mc = dumpKeysA ? 0x60 : 0x61;
if (!nfc_initiator_mifare_cmd(r.pdi, mc, t.sectors[j].trailer, &mp)) {
// fprintf(stdout, "!!Error: AUTH [Key A:%012llx] sector %02x t_block %02x, key %d\n",
// bytes_to_num(mp.mpa.abtKey, 6), j, t.sectors[j].trailer, o);
mf_anticollision(t, r);
} else {
// Save all information about successfull authentization
printf("Sector: %d, type %c\n", j, (dumpKeysA ? 'A' : 'B'));
if (dumpKeysA) {
memcpy(t.sectors[j].KeyA, mp.mpa.abtKey, sizeof(mp.mpa.abtKey));
t.sectors[j].foundKeyA = true;
// if we need KeyB for this sector it should be revealed by a data read with KeyA
if (!t.sectors[j].foundKeyB) {
if ((res = nfc_initiator_mifare_cmd(r.pdi, MC_READ, t.sectors[j].trailer, &mtmp)) >= 0) {
fprintf(stdout, " Data read with Key A revealed Key B: [%012llx] - checking Auth: ", bytes_to_num(mtmp.mpd.abtData + 10, sizeof(mtmp.mpa.abtKey)));
memcpy(mtmp.mpa.abtKey, mtmp.mpd.abtData + 10, sizeof(mtmp.mpa.abtKey));
memcpy(mtmp.mpa.abtAuthUid, t.nt.nti.nai.abtUid + t.nt.nti.nai.szUidLen - 4, sizeof(mtmp.mpa.abtAuthUid));
if ((res = nfc_initiator_mifare_cmd(r.pdi, MC_AUTH_B, t.sectors[j].trailer, &mtmp)) < 0) {
fprintf(stdout, "Failed!\n");
mf_configure(r.pdi);
mf_anticollision(t, r);
} else {
fprintf(stdout, "OK\n");
memcpy(t.sectors[j].KeyB, mtmp.mpd.abtData + 10, sizeof(t.sectors[j].KeyB));
t.sectors[j].foundKeyB = true;
bk->size++;
bk->brokenKeys = (uint64_t *) realloc((void *)bk->brokenKeys, bk->size * sizeof(uint64_t));
bk->brokenKeys[bk->size - 1] = bytes_to_num(mtmp.mpa.abtKey, sizeof(mtmp.mpa.abtKey));
}
} else {
if (res != NFC_ERFTRANS) {
nfc_perror(r.pdi, "nfc_initiator_mifare_cmd");
goto error;
}
mf_anticollision(t, r);
}
}
} else {
memcpy(t.sectors[j].KeyB, mp.mpa.abtKey, sizeof(mp.mpa.abtKey));
t.sectors[j].foundKeyB = true;
}
fprintf(stdout, " Found Key: %c [%012llx]\n", (dumpKeysA ? 'A' : 'B'),
printf("Sector: %d, type %c\n", j, (dumpKeysA ? 'A' : 'B'));
fprintf(stdout, "Found Key: %c [%012llx]\n", (dumpKeysA ? 'A' : 'B'),
bytes_to_num(mp.mpa.abtKey, 6));
mf_configure(r.pdi);
mf_anticollision(t, r);
@ -541,18 +367,7 @@ int main(int argc, char *const argv[])
// Max probes for auth for each sector
for (k = 0; k < probes; ++k) {
// Try to authenticate to exploit sector and determine distances (filling denonce.distances)
int authresult = mf_enhanced_auth(e_sector, 0, t, r, &d, pk, 'd', dumpKeysA); // AUTH + Get Distances mode
if(authresult == -99999){
//for now we return the last sector that is unknown
nfc_close(r.pdi);
nfc_exit(context);
if(pfKey) {
fprintf(pfKey, "%012llx;%d;%c;%d;%c", knownKey, knownSector, knownKeyLetter, unknownSector, unknownKeyLetter);
fclose(pfKey);
}
return 9;
}
mf_enhanced_auth(e_sector, 0, t, r, &d, pk, 'd', dumpKeysA); // AUTH + Get Distances mode
printf("Sector: %d, type %c, probe %d, distance %d ", j, (dumpKeysA ? 'A' : 'B'), k, d.median);
// Configure device to the previous state
mf_configure(r.pdi);
@ -579,19 +394,16 @@ int main(int argc, char *const argv[])
// fprintf(stdout,"%d %llx\n",ck[i].count, ck[i].key);
// Set required authetication method
num_to_bytes(ck[i].key, 6, mp.mpa.abtKey);
mc = dumpKeysA ? MC_AUTH_A : MC_AUTH_B;
int res;
if ((res = nfc_initiator_mifare_cmd(r.pdi, mc, t.sectors[j].trailer, &mp)) < 0) {
if (res != NFC_EMFCAUTHFAIL) {
nfc_perror(r.pdi, "nfc_initiator_mifare_cmd");
goto error;
}
mc = dumpKeysA ? 0x60 : 0x61;
if (!nfc_initiator_mifare_cmd(r.pdi, mc, t.sectors[j].trailer, &mp)) {
// fprintf(stdout, "!!Error: AUTH [Key A:%llx] sector %02x t_block %02x\n",
// bytes_to_num(mp.mpa.abtKey, 6), j, t.sectors[j].trailer);
mf_anticollision(t, r);
} else {
// Save all information about successfull authentization
bk->size++;
bk->brokenKeys = (uint64_t *) realloc((void *)bk->brokenKeys, bk->size * sizeof(uint64_t));
bk->brokenKeys[bk->size - 1] = bytes_to_num(mp.mpa.abtKey, sizeof(mp.mpa.abtKey));
bk->brokenKeys[bk->size - 1] = bytes_to_num(mp.mpa.abtKey, 6);
if (dumpKeysA) {
memcpy(t.sectors[j].KeyA, mp.mpa.abtKey, sizeof(mp.mpa.abtKey));
t.sectors[j].foundKeyA = true;
@ -600,34 +412,8 @@ int main(int argc, char *const argv[])
memcpy(t.sectors[j].KeyB, mp.mpa.abtKey, sizeof(mp.mpa.abtKey));
t.sectors[j].foundKeyB = true;
}
fprintf(stdout, " Found Key: %c [%012llx]\n", (dumpKeysA ? 'A' : 'B'),
fprintf(stdout, "Found Key: %c [%012llx]\n", (dumpKeysA ? 'A' : 'B'),
bytes_to_num(mp.mpa.abtKey, 6));
// if we need KeyB for this sector, it should be revealed by a data read with KeyA
if (!t.sectors[j].foundKeyB) {
if ((res = nfc_initiator_mifare_cmd(r.pdi, MC_READ, t.sectors[j].trailer, &mtmp)) >= 0) {
fprintf(stdout, " Data read with Key A revealed Key B: [%012llx] - checking Auth: ", bytes_to_num(mtmp.mpd.abtData + 10, sizeof(mtmp.mpa.abtKey)));
memcpy(mtmp.mpa.abtKey, mtmp.mpd.abtData + 10, sizeof(mtmp.mpa.abtKey));
memcpy(mtmp.mpa.abtAuthUid, t.nt.nti.nai.abtUid + t.nt.nti.nai.szUidLen - 4, sizeof(mtmp.mpa.abtAuthUid));
if ((res = nfc_initiator_mifare_cmd(r.pdi, MC_AUTH_B, t.sectors[j].trailer, &mtmp)) < 0) {
fprintf(stdout, "Failed!\n");
mf_configure(r.pdi);
mf_anticollision(t, r);
} else {
fprintf(stdout, "OK\n");
memcpy(t.sectors[j].KeyB, mtmp.mpd.abtData + 10, sizeof(t.sectors[j].KeyB));
t.sectors[j].foundKeyB = true;
bk->size++;
bk->brokenKeys = (uint64_t *) realloc((void *)bk->brokenKeys, bk->size * sizeof(uint64_t));
bk->brokenKeys[bk->size - 1] = bytes_to_num(mtmp.mpa.abtKey, sizeof(mtmp.mpa.abtKey));
}
} else {
if (res != NFC_ERFTRANS) {
nfc_perror(r.pdi, "nfc_initiator_mifare_cmd");
goto error;
}
mf_anticollision(t, r);
}
}
mf_configure(r.pdi);
mf_anticollision(t, r);
break;
@ -668,16 +454,12 @@ int main(int argc, char *const argv[])
// Try A key, auth() + read()
memcpy(mp.mpa.abtKey, t.sectors[i].KeyA, sizeof(t.sectors[i].KeyA));
int res;
if ((res = nfc_initiator_mifare_cmd(r.pdi, MC_AUTH_A, block, &mp)) < 0) {
if (res != NFC_EMFCAUTHFAIL) {
nfc_perror(r.pdi, "nfc_initiator_mifare_cmd");
goto error;
}
if (!nfc_initiator_mifare_cmd(r.pdi, MC_AUTH_A, block, &mp)) {
// ERR ("Error: Auth A");
mf_configure(r.pdi);
mf_anticollision(t, r);
} else { // and Read
if ((res = nfc_initiator_mifare_cmd(r.pdi, MC_READ, block, &mp)) >= 0) {
if (nfc_initiator_mifare_cmd(r.pdi, MC_READ, block, &mp)) {
fprintf(stdout, "Block %02d, type %c, key %012llx :", block, 'A', bytes_to_num(t.sectors[i].KeyA, 6));
print_hex(mp.mpd.abtData, 16);
mf_configure(r.pdi);
@ -685,32 +467,22 @@ int main(int argc, char *const argv[])
failure = false;
} else {
// Error, now try read() with B key
if (res != NFC_ERFTRANS) {
nfc_perror(r.pdi, "nfc_initiator_mifare_cmd");
goto error;
}
// ERR ("Error: Read A");
mf_configure(r.pdi);
mf_anticollision(t, r);
memcpy(mp.mpa.abtKey, t.sectors[i].KeyB, sizeof(t.sectors[i].KeyB));
if ((res = nfc_initiator_mifare_cmd(r.pdi, MC_AUTH_B, block, &mp)) < 0) {
if (res != NFC_EMFCAUTHFAIL) {
nfc_perror(r.pdi, "nfc_initiator_mifare_cmd");
goto error;
}
if (!nfc_initiator_mifare_cmd(r.pdi, MC_AUTH_B, block, &mp)) {
// ERR ("Error: Auth B");
mf_configure(r.pdi);
mf_anticollision(t, r);
} else { // and Read
if ((res = nfc_initiator_mifare_cmd(r.pdi, MC_READ, block, &mp)) >= 0) {
if (nfc_initiator_mifare_cmd(r.pdi, MC_READ, block, &mp)) {
fprintf(stdout, "Block %02d, type %c, key %012llx :", block, 'B', bytes_to_num(t.sectors[i].KeyB, 6));
print_hex(mp.mpd.abtData, 16);
mf_configure(r.pdi);
mf_select_tag(r.pdi, &(t.nt));
failure = false;
} else {
if (res != NFC_ERFTRANS) {
nfc_perror(r.pdi, "nfc_initiator_mifare_cmd");
goto error;
}
mf_configure(r.pdi);
mf_anticollision(t, r);
// ERR ("Error: Read B");
@ -728,8 +500,7 @@ int main(int argc, char *const argv[])
}
// Finally save all keys + data to file
uint16_t dump_size = (t.num_blocks + 1) * 16;
if (fwrite(&mtDump, 1, dump_size, pfDump) != dump_size) {
if (fwrite(&mtDump, 1, sizeof(mtDump), pfDump) != sizeof(mtDump)) {
fprintf(stdout, "Error, cannot write dump\n");
fclose(pfDump);
goto error;
@ -754,40 +525,33 @@ error:
exit(EXIT_FAILURE);
}
void usage(FILE *stream, int code)
void usage(FILE *stream, int errno)
{
fprintf(stream, "Usage: mfoc [-h] [-k key] [-f file] ... [-P probnum] [-T tolerance] [-O output]\n");
fprintf(stream, "Usage: mfoc [-h] [-k key]... [-P probnum] [-T tolerance] [-O output]\n");
fprintf(stream, "\n");
fprintf(stream, " h print this help and exit\n");
// fprintf(stream, " B instead of 'A' dump 'B' keys\n");
// fprintf(stream, " B instead of 'A' dump 'B' keys\n");
fprintf(stream, " k try the specified key in addition to the default keys\n");
fprintf(stream, " f parses a file of keys to add in addition to the default keys \n");
// fprintf(stream, " D number of distance probes, default is 20\n");
// fprintf(stream, " S number of sets with keystreams, default is 5\n");
// fprintf(stream, " D number of distance probes, default is 20\n");
// fprintf(stream, " S number of sets with keystreams, default is 5\n");
fprintf(stream, " P number of probes per sector, instead of default of 20\n");
fprintf(stream, " T nonce tolerance half-range, instead of default of 20\n (i.e., 40 for the total range, in both directions)\n");
// fprintf(stream, " s specify the list of sectors to crack, for example -s 0,1,3,5\n");
// fprintf(stream, " s specify the list of sectors to crack, for example -s 0,1,3,5\n");
fprintf(stream, " O file in which the card contents will be written (REQUIRED)\n");
fprintf(stream, " D file in which partial card info will be written in case PRNG is not vulnerable\n");
fprintf(stream, "\n");
fprintf(stream, "Example: mfoc -O mycard.mfd\n");
fprintf(stream, "Example: mfoc -k ffffeeeedddd -O mycard.mfd\n");
fprintf(stream, "Example: mfoc -f keys.txt -O mycard.mfd\n");
fprintf(stream, "Example: mfoc -P 50 -T 30 -O mycard.mfd\n");
fprintf(stream, "\n");
fprintf(stream, "This is mfoc version %s.\n", PACKAGE_VERSION);
fprintf(stream, "For more information, run: 'man mfoc'.\n");
exit(code);
exit(errno);
}
void mf_init(mfreader *r)
{
// Connect to the first NFC device
nfc_init(&context);
if (context == NULL) {
ERR("Unable to init libnfc (malloc)");
exit(EXIT_FAILURE);
}
r->pdi = nfc_open(context, NULL);
if (!r->pdi) {
printf("No NFC device found.\n");
@ -829,6 +593,11 @@ void mf_configure(nfc_device *pdi)
void mf_select_tag(nfc_device *pdi, nfc_target *pnt)
{
// Poll for a ISO14443A (MIFARE) tag
const nfc_modulation nm = {
.nmt = NMT_ISO14443A,
.nbr = NBR_106,
};
if (nfc_initiator_select_passive_target(pdi, nm, NULL, 0, pnt) < 0) {
ERR("Unable to connect to the MIFARE Classic tag");
nfc_close(pdi);
@ -871,6 +640,10 @@ int find_exploit_sector(mftag t)
void mf_anticollision(mftag t, mfreader r)
{
const nfc_modulation nm = {
.nmt = NMT_ISO14443A,
.nbr = NBR_106,
};
if (nfc_initiator_select_passive_target(r.pdi, nm, NULL, 0, &t.nt) < 0) {
nfc_perror(r.pdi, "nfc_initiator_select_passive_target");
ERR("Tag has been removed");
@ -878,48 +651,6 @@ void mf_anticollision(mftag t, mfreader r)
}
}
bool
get_rats_is_2k(mftag t, mfreader r)
{
int res;
uint8_t abtRx[MAX_FRAME_LEN];
int szRxBits;
uint8_t abtRats[2] = { 0xe0, 0x50};
// Use raw send/receive methods
if (nfc_device_set_property_bool(r.pdi, NP_EASY_FRAMING, false) < 0) {
nfc_perror(r.pdi, "nfc_configure");
return false;
}
res = nfc_initiator_transceive_bytes(r.pdi, abtRats, sizeof(abtRats), abtRx, sizeof(abtRx), 0);
if (res > 0) {
// ISO14443-4 card, turn RF field off/on to access ISO14443-3 again
if (nfc_device_set_property_bool(r.pdi, NP_ACTIVATE_FIELD, false) < 0) {
nfc_perror(r.pdi, "nfc_configure");
return false;
}
if (nfc_device_set_property_bool(r.pdi, NP_ACTIVATE_FIELD, true) < 0) {
nfc_perror(r.pdi, "nfc_configure");
return false;
}
}
// Reselect tag
if (nfc_initiator_select_passive_target(r.pdi, nm, NULL, 0, &t.nt) <= 0) {
printf("Error: tag disappeared\n");
nfc_close(r.pdi);
nfc_exit(context);
exit(EXIT_FAILURE);
}
if (res >= 10) {
printf("ATS %02X%02X%02X%02X%02X|%02X%02X%02X%02X%02X\n", res, abtRx[0], abtRx[1], abtRx[2], abtRx[3], abtRx[4], abtRx[5], abtRx[6], abtRx[7], abtRx[8]);
return ((abtRx[5] == 0xc1) && (abtRx[6] == 0x05)
&& (abtRx[7] == 0x2f) && (abtRx[8] == 0x2f)
&& ((t.nt.nti.nai.abtAtqa[1] & 0x02) == 0x00));
} else {
return false;
}
}
int mf_enhanced_auth(int e_sector, int a_sector, mftag t, mfreader r, denonce *d, pKeys *pk, char mode, bool dumpKeysA)
{
struct Crypto1State *pcs;
@ -944,11 +675,10 @@ int mf_enhanced_auth(int e_sector, int a_sector, mftag t, mfreader r, denonce *d
uint32_t Nt, NtLast, NtProbe, NtEnc, Ks1;
int i;
uint32_t m;
int i, m;
// Prepare AUTH command
Auth[0] = (t.sectors[e_sector].foundKeyA) ? MC_AUTH_A : MC_AUTH_B;
Auth[0] = (t.sectors[e_sector].foundKeyA) ? 0x60 : 0x61;
iso14443a_crc_append(Auth, 2);
// fprintf(stdout, "\nAuth command:\t");
// print_hex(Auth, 4);
@ -1059,13 +789,9 @@ int mf_enhanced_auth(int e_sector, int a_sector, mftag t, mfreader r, denonce *d
}
NtLast = bytes_to_num(Rx, 4) ^ crypto1_word(pcs, bytes_to_num(Rx, 4) ^ t.authuid, 1);
// Make sure the card is using the known PRNG
if (! validate_prng_nonce(NtLast)) {
printf("Card is not vulnerable to nested attack\n");
return -99999;
}
// Save the determined nonces distance
d->distances[m] = nonce_distance(Nt, NtLast);
// fprintf(stdout, "distance: %05d\n", d->distances[m]);
// Again, prepare and send {At}
for (i = 0; i < 4; i++) {
@ -1098,7 +824,7 @@ int mf_enhanced_auth(int e_sector, int a_sector, mftag t, mfreader r, denonce *d
// If we are in "Get Recovery" mode
if (mode == 'r') {
// Again, prepare the Auth command with MC_AUTH_A, recover the block and CRC
Auth[0] = dumpKeysA ? MC_AUTH_A : MC_AUTH_B;
Auth[0] = dumpKeysA ? 0x60 : 0x61;
Auth[1] = a_sector;
iso14443a_crc_append(Auth, 2);

View File

@ -2,24 +2,10 @@
#define TRY_KEYS 50
// Number of trailers == number of sectors
// Mifare Classic 1k 16x64b = 16
// 16x64b = 16
#define NR_TRAILERS_1k (16)
// Mifare Classic Mini
#define NR_TRAILERS_MINI (5)
// Mifare Classic 4k 32x64b + 8*256b = 40
// 32x64b + 8*256b = 40
#define NR_TRAILERS_4k (40)
// Mifare Classic 2k 32x64b
#define NR_TRAILERS_2k (32)
// Number of blocks
// Mifare Classic 1k
#define NR_BLOCKS_1k 0x3f
// Mifare Classic Mini
#define NR_BLOCKS_MINI 0x13
// Mifare Classic 4k
#define NR_BLOCKS_4k 0xff
// Mifare Classic 2k
#define NR_BLOCKS_2k 0x7f
#define MAX_FRAME_LEN 264
@ -50,46 +36,46 @@ typedef struct {
uint32_t median;
uint32_t num_distances;
uint32_t tolerance;
uint8_t parity[3]; // used for 3 bits of parity information
uint8_t parity[3]; // used for 3 bits of parity information
} denonce; // Revealed information about nonce
typedef struct {
nfc_target nt;
nfc_target nt;
sector *sectors; // Allocate later, we do not know the number of sectors yet
sector e_sector; // Exploit sector
sector e_sector; // Exploit sector
uint8_t num_sectors;
uint8_t num_blocks;
uint32_t authuid;
uint32_t authuid;
bool b4K;
} mftag;
typedef struct {
uint64_t *possibleKeys;
uint32_t size;
uint64_t *possibleKeys;
uint32_t size;
} pKeys;
typedef struct {
uint64_t *brokenKeys;
uint32_t size;
uint64_t *brokenKeys;
int32_t size;
} bKeys;
typedef struct {
nfc_device *pdi;
nfc_device *pdi;
} mfreader;
typedef struct {
uint64_t key;
int count;
uint64_t key;
int count;
} countKeys;
void usage(FILE *stream, int code);
void usage(FILE *stream, int errno);
void mf_init(mfreader *r);
void mf_configure(nfc_device *pdi);
void mf_select_tag(nfc_device *pdi, nfc_target *pnt);
int trailer_block(uint32_t block);
int find_exploit_sector(mftag t);
void mf_anticollision(mftag t, mfreader r);
bool get_rats_is_2k(mftag t, mfreader r);
int mf_enhanced_auth(int e_sector, int a_sector, mftag t, mfreader r, denonce *d, pKeys *pk, char mode, bool dumpKeysA);
uint32_t median(denonce d);
int compar_int(const void *a, const void *b);

View File

@ -3,7 +3,7 @@
*
* Copyright (C) 2009 Roel Verdult
* Copyright (C) 2010 Romain Tartière
* Copyright (C) 2010, 2011, 2013 Romuald Conty
* Copyright (C) 2010, 2011 Romuald Conty
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
@ -28,23 +28,10 @@
* Note that this license only applies on the examples, NFC library itself is under LGPL
*
*/
/**
* @file mifare.c
* @brief provide samples structs and functions to manipulate MIFARE Classic and Ultralight tags using libnfc
*/
/*
* This implementation was written based on information provided by the
* following document:
*
* MIFARE Classic Specification
* MF1ICS50
* Functional specification
* Rev. 5.3 - 29 January 2008
* http://www.nxp.com/acrobat/other/identification/M001053_MF1ICS50_rev5_3.pdf
*/
#include "mifare.h"
#include <string.h>
@ -52,33 +39,25 @@
#include <nfc/nfc.h>
/**
* @brief Execute a MIFARE Classic command
* @return Returns NFC_SUCCESS if action was successfully performed; otherwise returns error code (negative value).
* @brief Execute a MIFARE Classic Command
* @return Returns true if action was successfully performed; otherwise returns false.
* @param pmp Some commands need additional information. This information should be supplied in the mifare_param union.
*
* The specified MIFARE command will be executed on the tag. There are
* different commands possible, they all require the destination block number.
*
* The specified MIFARE command will be executed on the tag. There are different commands possible, they all require the destination block number.
* @note There are three different types of information (Authenticate, Data and Value).
*
* First an authentication must take place using Key A or B. It requires a 48 bit Key (6 bytes) and the UID.
* They are both used to initialize the internal cipher-state of the PN53X chip (http://libnfc.org/hardware/pn53x-chip).
* After a successful authentication it will be possible to execute other commands (e.g. Read/Write).
*
* Like libnfc's functions, this one returns negative value on error (libnfc's
* error code) but two of them need a special attention in this context (MIFARE
* Classic):
* - NFC_EMFCAUTHFAIL, "MIFARE authentication failed", means key is not valid
* on specified sector.
* - NFC_ERFTRANS, "Invalid received frame", when occurs on MIFARE command
* read or write after a successful authentication, means permissions allowed
* by current acces bytes are not sufficient to process the command.
* The MIFARE Classic Specification (http://www.nxp.com/acrobat/other/identification/M001053_MF1ICS50_rev5_3.pdf) explains more about this process.
*/
int
bool
nfc_initiator_mifare_cmd(nfc_device *pnd, const mifare_cmd mc, const uint8_t ui8Block, mifare_param *pmp)
{
uint8_t abtRx[265];
size_t szParamLen;
uint8_t abtCmd[265];
//bool bEasyFraming;
abtCmd[0] = mc; // The MIFARE Classic command
abtCmd[1] = ui8Block; // The block address (1K=0x00..0x39, 4K=0x00..0xff)
@ -110,7 +89,7 @@ nfc_initiator_mifare_cmd(nfc_device *pnd, const mifare_cmd mc, const uint8_t ui8
// Please fix your code, you never should reach this statement
default:
return NFC_EINVARG;
return false;
break;
}
@ -119,23 +98,40 @@ nfc_initiator_mifare_cmd(nfc_device *pnd, const mifare_cmd mc, const uint8_t ui8
memcpy(abtCmd + 2, (uint8_t *) pmp, szParamLen);
// FIXME: Save and restore bEasyFraming
int res;
if ((res = nfc_device_set_property_bool(pnd, NP_EASY_FRAMING, true)) < 0) {
return res;
// bEasyFraming = nfc_device_get_property_bool (pnd, NP_EASY_FRAMING, &bEasyFraming);
if (nfc_device_set_property_bool(pnd, NP_EASY_FRAMING, true) < 0) {
nfc_perror(pnd, "nfc_device_set_property_bool");
return false;
}
// Fire the mifare command
int res;
if ((res = nfc_initiator_transceive_bytes(pnd, abtCmd, 2 + szParamLen, abtRx, sizeof(abtRx), -1)) < 0) {
return res;
if (res == NFC_ERFTRANS) {
// "Invalid received frame", usual means we are
// authenticated on a sector but the requested MIFARE cmd (read, write)
// is not permitted by current acces bytes;
// So there is nothing to do here.
} else {
nfc_perror(pnd, "nfc_initiator_transceive_bytes");
}
// XXX nfc_device_set_property_bool (pnd, NP_EASY_FRAMING, bEasyFraming);
return false;
}
/* XXX
if (nfc_device_set_property_bool (pnd, NP_EASY_FRAMING, bEasyFraming) < 0) {
nfc_perror (pnd, "nfc_device_set_property_bool");
return false;
}
*/
// When we have executed a read command, copy the received bytes into the param
if (mc == MC_READ) {
if (res == 16) {
memcpy(pmp->mpd.abtData, abtRx, 16);
} else {
return NFC_EINVARG;
return false;
}
}
// Command succesfully executed
return NFC_SUCCESS;
return true;
}

View File

@ -3,7 +3,7 @@
*
* Copyright (C) 2009 Roel Verdult
* Copyright (C) 2010 Romain Tartière
* Copyright (C) 2010, 2011, 2013 Romuald Conty
* Copyright (C) 2010, 2011 Romuald Conty
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
@ -35,12 +35,12 @@
*/
#ifndef _LIBNFC_MIFARE_H_
#define _LIBNFC_MIFARE_H_
# define _LIBNFC_MIFARE_H_
#include <nfc/nfc-types.h>
# include <nfc/nfc-types.h>
// Compiler directive, set struct alignment to 1 uint8_t for compatibility
#pragma pack(1)
# pragma pack(1)
typedef enum {
MC_AUTH_A = 0x60,
@ -76,7 +76,7 @@ typedef union {
// Reset struct alignment to default
# pragma pack()
int nfc_initiator_mifare_cmd(nfc_device *pnd, const mifare_cmd mc, const uint8_t ui8Block, mifare_param *pmp);
bool nfc_initiator_mifare_cmd(nfc_device *pnd, const mifare_cmd mc, const uint8_t ui8Block, mifare_param *pmp);
// Compiler directive, set struct alignment to 1 uint8_t for compatibility
# pragma pack(1)

View File

@ -1,13 +1,9 @@
/*-
* Free/Libre Near Field Communication (NFC) library
* Public platform independent Near Field Communication (NFC) library examples
*
* Libnfc historical contributors:
* Copyright (C) 2009 Roel Verdult
* Copyright (C) 2009-2013 Romuald Conty
* Copyright (C) 2010-2012 Romain Tartière
* Copyright (C) 2010-2013 Philippe Teuwen
* Copyright (C) 2012-2013 Ludovic Rousseau
* Additional contributors of this file:
* Copyright (C) 2009 Roel Verdult
* Copyright (C) 2010, 2011 Romain Tartière
* Copyright (C) 2009, 2010, 2011, 2012 Romuald Conty
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
@ -119,10 +115,10 @@ print_hex_par(const uint8_t *pbtData, const size_t szBits, const uint8_t *pbtDat
}
void
print_nfc_target(const nfc_target *pnt, bool verbose)
print_nfc_target(const nfc_target nt, bool verbose)
{
char *s;
str_nfc_target(&s, pnt, verbose);
str_nfc_target(&s, nt, verbose);
printf("%s", s);
nfc_free(s);
free(s);
}

View File

@ -1,13 +1,9 @@
/*-
* Free/Libre Near Field Communication (NFC) library
* Public platform independent Near Field Communication (NFC) library examples
*
* Libnfc historical contributors:
* Copyright (C) 2009 Roel Verdult
* Copyright (C) 2009-2013 Romuald Conty
* Copyright (C) 2010-2012 Romain Tartière
* Copyright (C) 2010-2013 Philippe Teuwen
* Copyright (C) 2012-2013 Ludovic Rousseau
* Additional contributors of this file:
* Copyright (C) 2009 Roel Verdult
* Copyright (C) 2010 Romain Tartière
* Copyright (C) 2010, 2011, 2012 Romuald Conty
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
@ -98,6 +94,6 @@ void print_hex(const uint8_t *pbtData, const size_t szLen);
void print_hex_bits(const uint8_t *pbtData, const size_t szBits);
void print_hex_par(const uint8_t *pbtData, const size_t szBits, const uint8_t *pbtDataPar);
void print_nfc_target(const nfc_target *pnt, bool verbose);
void print_nfc_target(const nfc_target nt, bool verbose);
#endif

View File

@ -1,437 +0,0 @@
/*
* Copyright (c) 2004-2013 Sergey Lyubka <valenok@gmail.com>
* Copyright (c) 2013 Cesanta Software Limited
* All rights reserved
*
* This library is dual-licensed: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation. For the terms of this
* license, see <http://www.gnu.org/licenses/>.
*
* You are free to use this library under the terms of the GNU General
* Public License, 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.
*
* Alternatively, you can license this library under a commercial
* license, as set out in <http://cesanta.com/products.html>.
*/
#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include "slre.h"
#define MAX_BRANCHES 100
#define MAX_BRACKETS 100
#define FAIL_IF(condition, error_code) if (condition) return (error_code)
#ifndef ARRAY_SIZE
#define ARRAY_SIZE(ar) (sizeof(ar) / sizeof((ar)[0]))
#endif
#ifdef SLRE_DEBUG
# ifndef DBG
# define DBG(x) printf x
# endif
#else
# ifndef DBG
# define DBG(x)
# endif
#endif
struct bracket_pair {
const char *ptr; /* Points to the first char after '(' in regex */
int len; /* Length of the text between '(' and ')' */
int branches; /* Index in the branches array for this pair */
int num_branches; /* Number of '|' in this bracket pair */
};
struct branch {
int bracket_index; /* index for 'struct bracket_pair brackets' */
/* array defined below */
const char *schlong; /* points to the '|' character in the regex */
};
struct regex_info {
/*
* Describes all bracket pairs in the regular expression.
* First entry is always present, and grabs the whole regex.
*/
struct bracket_pair brackets[MAX_BRACKETS];
int num_brackets;
/*
* Describes alternations ('|' operators) in the regular expression.
* Each branch falls into a specific branch pair.
*/
struct branch branches[MAX_BRANCHES];
int num_branches;
/* Array of captures provided by the user */
struct slre_cap *caps;
int num_caps;
/* E.g. SLRE_IGNORE_CASE. See enum below */
int flags;
};
static int is_metacharacter(const unsigned char *s) {
static const char *metacharacters = "^$().[]*+?|\\Ssdbfnrtv";
return strchr(metacharacters, *s) != NULL;
}
static int op_len(const char *re) {
return re[0] == '\\' && re[1] == 'x' ? 4 : re[0] == '\\' ? 2 : 1;
}
static int set_len(const char *re, int re_len) {
int len = 0;
while (len < re_len && re[len] != ']') {
len += op_len(re + len);
}
return len <= re_len ? len + 1 : -1;
}
static int get_op_len(const char *re, int re_len) {
return re[0] == '[' ? set_len(re + 1, re_len - 1) + 1 : op_len(re);
}
static int is_quantifier(const char *re) {
return re[0] == '*' || re[0] == '+' || re[0] == '?';
}
static int toi(int x) {
return isdigit(x) ? x - '0' : x - 'W';
}
static int hextoi(const unsigned char *s) {
return (toi(tolower(s[0])) << 4) | toi(tolower(s[1]));
}
static int match_op(const unsigned char *re, const unsigned char *s,
struct regex_info *info) {
int result = 0;
switch (*re) {
case '\\':
/* Metacharacters */
switch (re[1]) {
case 'S': FAIL_IF(isspace(*s), SLRE_NO_MATCH); result++; break;
case 's': FAIL_IF(!isspace(*s), SLRE_NO_MATCH); result++; break;
case 'd': FAIL_IF(!isdigit(*s), SLRE_NO_MATCH); result++; break;
case 'b': FAIL_IF(*s != '\b', SLRE_NO_MATCH); result++; break;
case 'f': FAIL_IF(*s != '\f', SLRE_NO_MATCH); result++; break;
case 'n': FAIL_IF(*s != '\n', SLRE_NO_MATCH); result++; break;
case 'r': FAIL_IF(*s != '\r', SLRE_NO_MATCH); result++; break;
case 't': FAIL_IF(*s != '\t', SLRE_NO_MATCH); result++; break;
case 'v': FAIL_IF(*s != '\v', SLRE_NO_MATCH); result++; break;
case 'x':
/* Match byte, \xHH where HH is hexadecimal byte representaion */
FAIL_IF(hextoi(re + 2) != *s, SLRE_NO_MATCH);
result++;
break;
default:
/* Valid metacharacter check is done in bar() */
FAIL_IF(re[1] != s[0], SLRE_NO_MATCH);
result++;
break;
}
break;
case '|': FAIL_IF(1, SLRE_INTERNAL_ERROR); break;
case '$': FAIL_IF(1, SLRE_NO_MATCH); break;
case '.': result++; break;
default:
if (info->flags & SLRE_IGNORE_CASE) {
FAIL_IF(tolower(*re) != tolower(*s), SLRE_NO_MATCH);
} else {
FAIL_IF(*re != *s, SLRE_NO_MATCH);
}
result++;
break;
}
return result;
}
static int match_set(const char *re, int re_len, const char *s,
struct regex_info *info) {
int len = 0, result = -1, invert = re[0] == '^';
if (invert) re++, re_len--;
while (len <= re_len && re[len] != ']' && result <= 0) {
/* Support character range */
if (re[len] != '-' && re[len + 1] == '-' && re[len + 2] != ']' &&
re[len + 2] != '\0') {
result = info->flags && SLRE_IGNORE_CASE ?
*s >= re[len] && *s <= re[len + 2] :
tolower(*s) >= tolower(re[len]) && tolower(*s) <= tolower(re[len + 2]);
len += 3;
} else {
result = match_op((unsigned char *) re + len, (unsigned char *) s, info);
len += op_len(re + len);
}
}
return (!invert && result > 0) || (invert && result <= 0) ? 1 : -1;
}
static int doh(const char *s, int s_len, struct regex_info *info, int bi);
static int bar(const char *re, int re_len, const char *s, int s_len,
struct regex_info *info, int bi) {
/* i is offset in re, j is offset in s, bi is brackets index */
int i, j, n, step;
for (i = j = 0; i < re_len && j <= s_len; i += step) {
/* Handle quantifiers. Get the length of the chunk. */
step = re[i] == '(' ? info->brackets[bi + 1].len + 2 :
get_op_len(re + i, re_len - i);
DBG(("%s [%.*s] [%.*s] re_len=%d step=%d i=%d j=%d\n", __func__,
re_len - i, re + i, s_len - j, s + j, re_len, step, i, j));
FAIL_IF(is_quantifier(&re[i]), SLRE_UNEXPECTED_QUANTIFIER);
FAIL_IF(step <= 0, SLRE_INVALID_CHARACTER_SET);
if (i + step < re_len && is_quantifier(re + i + step)) {
DBG(("QUANTIFIER: [%.*s]%c [%.*s]\n", step, re + i,
re[i + step], s_len - j, s + j));
if (re[i + step] == '?') {
int result = bar(re + i, step, s + j, s_len - j, info, bi);
j += result > 0 ? result : 0;
i++;
} else if (re[i + step] == '+' || re[i + step] == '*') {
int j2 = j, nj = j, n1, n2 = -1, ni, non_greedy = 0;
/* Points to the regexp code after the quantifier */
ni = i + step + 1;
if (ni < re_len && re[ni] == '?') {
non_greedy = 1;
ni++;
}
do {
if ((n1 = bar(re + i, step, s + j2, s_len - j2, info, bi)) > 0) {
j2 += n1;
}
if (re[i + step] == '+' && n1 < 0) break;
if (ni >= re_len) {
/* After quantifier, there is nothing */
nj = j2;
} else if ((n2 = bar(re + ni, re_len - ni, s + j2,
s_len - j2, info, bi)) >= 0) {
/* Regex after quantifier matched */
nj = j2 + n2;
}
if (nj > j && non_greedy) break;
} while (n1 > 0);
if (n1 < 0 && re[i + step] == '*' &&
(n2 = bar(re + ni, re_len - ni, s + j, s_len - j, info, bi)) > 0) {
nj = j + n2;
}
DBG(("STAR/PLUS END: %d %d %d %d %d\n", j, nj, re_len - ni, n1, n2));
FAIL_IF(re[i + step] == '+' && nj == j, SLRE_NO_MATCH);
/* If while loop body above was not executed for the * quantifier, */
/* make sure the rest of the regex matches */
FAIL_IF(nj == j && ni < re_len && n2 < 0, SLRE_NO_MATCH);
/* Returning here cause we've matched the rest of RE already */
return nj;
}
continue;
}
if (re[i] == '[') {
n = match_set(re + i + 1, re_len - (i + 2), s + j, info);
DBG(("SET %.*s [%.*s] -> %d\n", step, re + i, s_len - j, s + j, n));
FAIL_IF(n <= 0, SLRE_NO_MATCH);
j += n;
} else if (re[i] == '(') {
n = SLRE_NO_MATCH;
bi++;
FAIL_IF(bi >= info->num_brackets, SLRE_INTERNAL_ERROR);
DBG(("CAPTURING [%.*s] [%.*s] [%s]\n",
step, re + i, s_len - j, s + j, re + i + step));
if (re_len - (i + step) <= 0) {
/* Nothing follows brackets */
n = doh(s + j, s_len - j, info, bi);
} else {
int j2;
for (j2 = 0; j2 <= s_len - j; j2++) {
if ((n = doh(s + j, s_len - (j + j2), info, bi)) >= 0 &&
bar(re + i + step, re_len - (i + step),
s + j + n, s_len - (j + n), info, bi) >= 0) break;
}
}
DBG(("CAPTURED [%.*s] [%.*s]:%d\n", step, re + i, s_len - j, s + j, n));
FAIL_IF(n < 0, n);
if (info->caps != NULL) {
info->caps[bi - 1].ptr = s + j;
info->caps[bi - 1].len = n;
}
j += n;
} else if (re[i] == '^') {
FAIL_IF(j != 0, SLRE_NO_MATCH);
} else if (re[i] == '$') {
FAIL_IF(j != s_len, SLRE_NO_MATCH);
} else {
FAIL_IF(j >= s_len, SLRE_NO_MATCH);
n = match_op((unsigned char *) (re + i), (unsigned char *) (s + j), info);
FAIL_IF(n <= 0, n);
j += n;
}
}
return j;
}
/* Process branch points */
static int doh(const char *s, int s_len, struct regex_info *info, int bi) {
const struct bracket_pair *b = &info->brackets[bi];
int i = 0, len, result;
const char *p;
do {
p = i == 0 ? b->ptr : info->branches[b->branches + i - 1].schlong + 1;
len = b->num_branches == 0 ? b->len :
i == b->num_branches ? (int) (b->ptr + b->len - p) :
(int) (info->branches[b->branches + i].schlong - p);
DBG(("%s %d %d [%.*s] [%.*s]\n", __func__, bi, i, len, p, s_len, s));
result = bar(p, len, s, s_len, info, bi);
DBG(("%s <- %d\n", __func__, result));
} while (result <= 0 && i++ < b->num_branches); /* At least 1 iteration */
return result;
}
static int baz(const char *s, int s_len, struct regex_info *info) {
int i, result = -1, is_anchored = info->brackets[0].ptr[0] == '^';
for (i = 0; i <= s_len; i++) {
result = doh(s + i, s_len - i, info, 0);
if (result >= 0) {
result += i;
break;
}
if (is_anchored) break;
}
return result;
}
static void setup_branch_points(struct regex_info *info) {
int i, j;
struct branch tmp;
/* First, sort branches. Must be stable, no qsort. Use bubble algo. */
for (i = 0; i < info->num_branches; i++) {
for (j = i + 1; j < info->num_branches; j++) {
if (info->branches[i].bracket_index > info->branches[j].bracket_index) {
tmp = info->branches[i];
info->branches[i] = info->branches[j];
info->branches[j] = tmp;
}
}
}
/*
* For each bracket, set their branch points. This way, for every bracket
* (i.e. every chunk of regex) we know all branch points before matching.
*/
for (i = j = 0; i < info->num_brackets; i++) {
info->brackets[i].num_branches = 0;
info->brackets[i].branches = j;
while (j < info->num_branches && info->branches[j].bracket_index == i) {
info->brackets[i].num_branches++;
j++;
}
}
}
static int foo(const char *re, int re_len, const char *s, int s_len,
struct regex_info *info) {
int i, step, depth = 0;
/* First bracket captures everything */
info->brackets[0].ptr = re;
info->brackets[0].len = re_len;
info->num_brackets = 1;
/* Make a single pass over regex string, memorize brackets and branches */
for (i = 0; i < re_len; i += step) {
step = get_op_len(re + i, re_len - i);
if (re[i] == '|') {
FAIL_IF(info->num_branches >= (int) ARRAY_SIZE(info->branches),
SLRE_TOO_MANY_BRANCHES);
info->branches[info->num_branches].bracket_index =
info->brackets[info->num_brackets - 1].len == -1 ?
info->num_brackets - 1 : depth;
info->branches[info->num_branches].schlong = &re[i];
info->num_branches++;
} else if (re[i] == '\\') {
FAIL_IF(i >= re_len - 1, SLRE_INVALID_METACHARACTER);
if (re[i + 1] == 'x') {
/* Hex digit specification must follow */
FAIL_IF(re[i + 1] == 'x' && i >= re_len - 3,
SLRE_INVALID_METACHARACTER);
FAIL_IF(re[i + 1] == 'x' && !(isxdigit(re[i + 2]) &&
isxdigit(re[i + 3])), SLRE_INVALID_METACHARACTER);
} else {
FAIL_IF(!is_metacharacter((unsigned char *) re + i + 1),
SLRE_INVALID_METACHARACTER);
}
} else if (re[i] == '(') {
FAIL_IF(info->num_brackets >= (int) ARRAY_SIZE(info->brackets),
SLRE_TOO_MANY_BRACKETS);
depth++; /* Order is important here. Depth increments first. */
info->brackets[info->num_brackets].ptr = re + i + 1;
info->brackets[info->num_brackets].len = -1;
info->num_brackets++;
FAIL_IF(info->num_caps > 0 && info->num_brackets - 1 > info->num_caps,
SLRE_CAPS_ARRAY_TOO_SMALL);
} else if (re[i] == ')') {
int ind = info->brackets[info->num_brackets - 1].len == -1 ?
info->num_brackets - 1 : depth;
info->brackets[ind].len = (int) (&re[i] - info->brackets[ind].ptr);
DBG(("SETTING BRACKET %d [%.*s]\n",
ind, info->brackets[ind].len, info->brackets[ind].ptr));
depth--;
FAIL_IF(depth < 0, SLRE_UNBALANCED_BRACKETS);
FAIL_IF(i > 0 && re[i - 1] == '(', SLRE_NO_MATCH);
}
}
FAIL_IF(depth != 0, SLRE_UNBALANCED_BRACKETS);
setup_branch_points(info);
return baz(s, s_len, info);
}
int slre_match(const char *regexp, const char *s, int s_len,
struct slre_cap *caps, int num_caps, int flags) {
struct regex_info info;
/* Initialize info structure */
info.flags = flags;
info.num_brackets = info.num_branches = 0;
info.num_caps = num_caps;
info.caps = caps;
DBG(("========================> [%s] [%.*s]\n", regexp, s_len, s));
return foo(regexp, (int) strlen(regexp), s, s_len, &info);
}

View File

@ -1,60 +0,0 @@
/*
* Copyright (c) 2004-2013 Sergey Lyubka <valenok@gmail.com>
* Copyright (c) 2013 Cesanta Software Limited
* All rights reserved
*
* This library is dual-licensed: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation. For the terms of this
* license, see <http://www.gnu.org/licenses/>.
*
* You are free to use this library under the terms of the GNU General
* Public License, 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.
*
* Alternatively, you can license this library under a commercial
* license, as set out in <http://cesanta.com/products.html>.
*/
/*
* This is a regular expression library that implements a subset of Perl RE.
* Please refer to README.md for a detailed reference.
*/
#ifndef SLRE_HEADER_DEFINED
#define SLRE_HEADER_DEFINED
#ifdef __cplusplus
extern "C" {
#endif
struct slre_cap {
const char *ptr;
int len;
};
int slre_match(const char *regexp, const char *buf, int buf_len,
struct slre_cap *caps, int num_caps, int flags);
/* Possible flags for slre_match() */
enum { SLRE_IGNORE_CASE = 1 };
/* slre_match() failure codes */
#define SLRE_NO_MATCH -1
#define SLRE_UNEXPECTED_QUANTIFIER -2
#define SLRE_UNBALANCED_BRACKETS -3
#define SLRE_INTERNAL_ERROR -4
#define SLRE_INVALID_CHARACTER_SET -5
#define SLRE_INVALID_METACHARACTER -6
#define SLRE_CAPS_ARRAY_TOO_SMALL -7
#define SLRE_TOO_MANY_BRANCHES -8
#define SLRE_TOO_MANY_BRACKETS -9
#ifdef __cplusplus
}
#endif
#endif /* SLRE_HEADER_DEFINED */