Compare commits
62 commits
mfoc-0.10.
...
master
Author | SHA1 | Date | |
---|---|---|---|
Davide Depau | 5524dd8af4 | ||
ba072f16f6 | |||
dd0ce5caa6 | |||
987c3dab3a | |||
77db13ef90 | |||
519e475b09 | |||
fa3573cfb6 | |||
c24b6ac572 | |||
593315f7d4 | |||
9fed51206f | |||
809f3bc62c | |||
b13beeac09 | |||
9d9f01fba4 | |||
34d42e5e47 | |||
2316ad0815 | |||
0970559b97 | |||
e36025bb25 | |||
f172064f98 | |||
48156f9bf0 | |||
e2cf90202a | |||
eac78225eb | |||
51bae1e1c8 | |||
0976e6f285 | |||
eff3dc0d5f | |||
7ff1465409 | |||
c346e2af72 | |||
86caca5f6b | |||
00eae36f89 | |||
3b5be84676 | |||
f3ebde09ef | |||
b872a328e3 | |||
30ce00aa8e | |||
f3a793dc0c | |||
5d8bf95968 | |||
1eac72641c | |||
f3558144d8 | |||
f13efb0a6d | |||
a1be79d0ff | |||
b31ac50224 | |||
290a075956 | |||
2fa70fb3d3 | |||
e1a2b0225f | |||
222ba1838c | |||
fa47ca0223 | |||
9b37ccd725 | |||
e5024608f3 | |||
ba7e75cd16 | |||
0c2d2b5894 | |||
9a02d34ede | |||
6b65fb4ca2 | |||
0406f0002e | |||
b1bc800c83 | |||
166a1467c2 | |||
2b16dc7ff8 | |||
35489b09a1 | |||
ab826c9fe8 | |||
e52c737242 | |||
205050848c | |||
ed16dec250 | |||
d10f2e0bdb | |||
1c5af20e98 | |||
d65d57d06e |
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -8,6 +8,7 @@ config.h.in
|
|||
config.log
|
||||
config.status
|
||||
configure
|
||||
compile
|
||||
depcomp
|
||||
install-sh
|
||||
missing
|
||||
|
@ -17,5 +18,6 @@ src/.deps/
|
|||
src/Makefile
|
||||
src/Makefile.in
|
||||
src/mfoc
|
||||
src/mfoc.exe
|
||||
stamp-h1
|
||||
|
||||
|
|
20
README.md
Normal file
20
README.md
Normal file
|
@ -0,0 +1,20 @@
|
|||
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
|
||||
```
|
|
@ -1,14 +1,14 @@
|
|||
AC_INIT([mfoc],[0.10.4],[mifare@nethemba.com])
|
||||
AC_INIT([mfoc],[0.10.7],[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
|
||||
AM_INIT_AUTOMAKE(dist-bzip2 no-dist-gzip)
|
||||
|
||||
AC_PROG_CC
|
||||
|
||||
m4_ifdef([AM_SILENT_RULES],[AM_SILENT_RULES([yes])])
|
||||
|
||||
|
|
6
debian/.gitignore
vendored
6
debian/.gitignore
vendored
|
@ -1,6 +0,0 @@
|
|||
autoreconf.after
|
||||
autoreconf.before
|
||||
files
|
||||
mfoc.debhelper.log
|
||||
mfoc.substvars
|
||||
mfoc/
|
68
debian/changelog
vendored
68
debian/changelog
vendored
|
@ -1,11 +1,67 @@
|
|||
mfoc (0.10.4-0) unstable; urgency=low
|
||||
mfoc (0.10.7+git20180724-1) unstable; urgency=medium
|
||||
|
||||
* New upstream release
|
||||
* New upstream version 0.10.7+git20180724
|
||||
|
||||
-- Romuald Conty <romuald@libnfc.org> Sun, 20 Jan 2013 15:58:42 +0100
|
||||
-- Samuel Henrique <samueloph@debian.org> Tue, 24 Jul 2018 01:19:50 -0300
|
||||
|
||||
mfoc (0.10.2pre3.1-0) unstable; urgency=low
|
||||
mfoc (0.10.7+git20150512-0kali1) kali; urgency=medium
|
||||
|
||||
* Initial package
|
||||
* Import upstream (Closes: 0002240)
|
||||
* Update debian files: watch, copyright
|
||||
* Use debhelper 9
|
||||
|
||||
-- Thomas Hood <jdthood@gmail.com> Wed, 18 May 2011 12:00:00 +0200
|
||||
-- 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
|
||||
|
|
2
debian/compat
vendored
2
debian/compat
vendored
|
@ -1 +1 @@
|
|||
7
|
||||
11
|
||||
|
|
15
debian/control
vendored
15
debian/control
vendored
|
@ -1,12 +1,13 @@
|
|||
Source: mfoc
|
||||
Section: utils
|
||||
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
|
||||
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
|
||||
|
||||
Package: mfoc
|
||||
Architecture: any
|
||||
|
|
75
debian/copyright
vendored
75
debian/copyright
vendored
|
@ -1,11 +1,59 @@
|
|||
Format: http://dep.debian.net/deps/dep5
|
||||
Upstream-Name: MFOC
|
||||
Source: http://nfc-tools.googlecode.com/svn/trunk/mfoc
|
||||
Source: https://github.com/nfc-tools/mfoc
|
||||
|
||||
Files: *
|
||||
Copyright: 2009 Norbert Szetei, Pavol Luptak
|
||||
2010 Micahal Boska, Romuald Conty
|
||||
2011 Romuald Conty
|
||||
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+
|
||||
|
||||
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
|
||||
|
@ -23,22 +71,3 @@ 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
14
debian/rules
vendored
|
@ -1,16 +1,8 @@
|
|||
#!/usr/bin/make -f
|
||||
# -*- 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.
|
||||
export DEB_BUILD_MAINT_OPTIONS = hardening=+all
|
||||
|
||||
# Uncomment this to turn on verbose mode.
|
||||
#export DH_VERBOSE=1
|
||||
%:
|
||||
dh $@
|
||||
|
||||
override_dh_installchangelogs:
|
||||
dh_installchangelogs ChangeLog
|
||||
|
||||
%:
|
||||
dh --with autoreconf $@
|
||||
|
|
8
debian/watch
vendored
8
debian/watch
vendored
|
@ -1,7 +1,3 @@
|
|||
# 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
|
||||
version=4
|
||||
|
||||
https://github.com/nfc-tools/mfoc/tags/ .*/mfoc-(.*)\.tar\.gz
|
||||
|
|
|
@ -311,7 +311,6 @@ 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
|
||||
|
@ -375,6 +374,12 @@ 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] = {
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
#ifndef CRAPTO1_INCLUDED
|
||||
#define CRAPTO1_INCLUDED
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
@ -39,6 +40,7 @@ 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;\
|
||||
|
@ -62,13 +64,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
|
||||
}
|
||||
|
|
508
src/mfoc.c
508
src/mfoc.c
|
@ -1,34 +1,36 @@
|
|||
/*-
|
||||
* 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>
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
|
||||
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
|
||||
*/
|
||||
* 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
|
||||
*/
|
||||
|
||||
#define _XOPEN_SOURCE 1 // To enable getopt
|
||||
|
||||
|
@ -50,15 +52,28 @@
|
|||
#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;
|
||||
|
@ -81,43 +96,55 @@ int main(int argc, char *const argv[])
|
|||
uint8_t defaultKeys[][6] = {
|
||||
{0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, // Default key (first key used by program if no user defined key)
|
||||
{0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5}, // NFCForum MAD key
|
||||
{0xd3, 0xf7, 0xd3, 0xf7, 0xd3, 0xf7}, // NFCForum content key
|
||||
{0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // Blank key
|
||||
{0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5},
|
||||
{0x4d, 0x3a, 0x99, 0xc3, 0x51, 0xdd},
|
||||
{0x1a, 0x98, 0x2c, 0x7e, 0x45, 0x9a},
|
||||
{0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff},
|
||||
{0x71, 0x4c, 0x5c, 0x88, 0x6e, 0x97},
|
||||
{0x58, 0x7e, 0xe5, 0xf9, 0x35, 0x0f},
|
||||
{0xa0, 0x47, 0x8c, 0xc3, 0x90, 0x91},
|
||||
{0x53, 0x3c, 0xb6, 0xc7, 0x23, 0xf6},
|
||||
{0x8f, 0xd0, 0xa4, 0xf2, 0x56, 0xe9}
|
||||
// {0xd3, 0xf7, 0xd3, 0xf7, 0xd3, 0xf7}, // NFCForum content key
|
||||
// {0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // Blank key
|
||||
// {0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5},
|
||||
// {0x4d, 0x3a, 0x99, 0xc3, 0x51, 0xdd},
|
||||
// {0x1a, 0x98, 0x2c, 0x7e, 0x45, 0x9a},
|
||||
// {0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff},
|
||||
// {0x71, 0x4c, 0x5c, 0x88, 0x6e, 0x97},
|
||||
// {0x58, 0x7e, 0xe5, 0xf9, 0x35, 0x0f},
|
||||
// {0xa0, 0x47, 0x8c, 0xc3, 0x90, 0x91},
|
||||
// {0x53, 0x3c, 0xb6, 0xc7, 0x23, 0xf6},
|
||||
// {0x8f, 0xd0, 0xa4, 0xf2, 0x56, 0xe9}
|
||||
|
||||
};
|
||||
|
||||
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;
|
||||
static mifare_param mp, mtmp;
|
||||
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:")) != -1) {
|
||||
while ((ch = getopt(argc, argv, "hD:s:BP:T:S:O:k:t:f:")) != -1) {
|
||||
switch (ch) {
|
||||
case 'P':
|
||||
// Number of probes
|
||||
if (!(probes = atoi(optarg)) || probes < 1) {
|
||||
probes = atoi(optarg);
|
||||
if (probes < 0) {
|
||||
ERR("The number of probes must be a positive number");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
@ -134,6 +161,31 @@ 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);
|
||||
|
@ -156,6 +208,14 @@ 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;
|
||||
|
@ -198,28 +258,58 @@ 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);
|
||||
*/
|
||||
|
||||
// mf_select_tag(r.pdi, &(t.nt));
|
||||
if (nfc_initiator_select_passive_target(r.pdi, nm, NULL, 0, &t.nt) < 0) {
|
||||
int tag_count;
|
||||
if ((tag_count = 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) {
|
||||
if (((t.nt.nti.nai.btSak & 0x08) == 0) && (t.nt.nti.nai.btSak != 0x01)) {
|
||||
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);
|
||||
|
||||
t.num_blocks = (t.b4K) ? 0xff : 0x3f;
|
||||
t.num_sectors = t.b4K ? NR_TRAILERS_4k : NR_TRAILERS_1k;
|
||||
// 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:
|
||||
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.sectors = (void *) calloc(t.num_sectors, sizeof(sector));
|
||||
if (t.sectors == NULL) {
|
||||
|
@ -249,16 +339,17 @@ 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);
|
||||
|
||||
// Try to authenticate to all sectors with default keys
|
||||
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");
|
||||
// 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) {
|
||||
while (key < n || defKey_bytes_todo) {
|
||||
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);
|
||||
|
@ -274,21 +365,58 @@ int main(int argc, char *const argv[])
|
|||
if (trailer_block(block)) {
|
||||
if (!t.sectors[i].foundKeyA) {
|
||||
mc = MC_AUTH_A;
|
||||
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);
|
||||
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;
|
||||
}
|
||||
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;
|
||||
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);
|
||||
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;
|
||||
}
|
||||
mf_anticollision(t, r);
|
||||
// No success, try next block
|
||||
t.sectors[i].trailer = block;
|
||||
|
@ -307,8 +435,6 @@ 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;
|
||||
}
|
||||
|
@ -318,9 +444,28 @@ int main(int argc, char *const argv[])
|
|||
|
||||
fprintf(stdout, "\n");
|
||||
for (i = 0; i < (t.num_sectors); ++i) {
|
||||
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");
|
||||
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';
|
||||
}
|
||||
}
|
||||
fflush(stdout);
|
||||
|
||||
|
@ -339,22 +484,51 @@ 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 ? 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);
|
||||
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;
|
||||
}
|
||||
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;
|
||||
}
|
||||
printf("Sector: %d, type %c\n", j, (dumpKeysA ? 'A' : 'B'));
|
||||
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));
|
||||
mf_configure(r.pdi);
|
||||
mf_anticollision(t, r);
|
||||
|
@ -367,7 +541,18 @@ 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)
|
||||
mf_enhanced_auth(e_sector, 0, t, r, &d, pk, 'd', dumpKeysA); // AUTH + Get Distances mode
|
||||
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;
|
||||
}
|
||||
|
||||
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);
|
||||
|
@ -394,16 +579,19 @@ 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 ? 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);
|
||||
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;
|
||||
}
|
||||
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, 6);
|
||||
bk->brokenKeys[bk->size - 1] = bytes_to_num(mp.mpa.abtKey, sizeof(mp.mpa.abtKey));
|
||||
if (dumpKeysA) {
|
||||
memcpy(t.sectors[j].KeyA, mp.mpa.abtKey, sizeof(mp.mpa.abtKey));
|
||||
t.sectors[j].foundKeyA = true;
|
||||
|
@ -412,8 +600,34 @@ 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;
|
||||
|
@ -454,12 +668,16 @@ 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));
|
||||
if (!nfc_initiator_mifare_cmd(r.pdi, MC_AUTH_A, block, &mp)) {
|
||||
// ERR ("Error: Auth A");
|
||||
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;
|
||||
}
|
||||
mf_configure(r.pdi);
|
||||
mf_anticollision(t, r);
|
||||
} else { // and Read
|
||||
if (nfc_initiator_mifare_cmd(r.pdi, MC_READ, block, &mp)) {
|
||||
if ((res = nfc_initiator_mifare_cmd(r.pdi, MC_READ, block, &mp)) >= 0) {
|
||||
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);
|
||||
|
@ -467,22 +685,32 @@ int main(int argc, char *const argv[])
|
|||
failure = false;
|
||||
} else {
|
||||
// Error, now try read() with B key
|
||||
// ERR ("Error: Read A");
|
||||
if (res != NFC_ERFTRANS) {
|
||||
nfc_perror(r.pdi, "nfc_initiator_mifare_cmd");
|
||||
goto error;
|
||||
}
|
||||
mf_configure(r.pdi);
|
||||
mf_anticollision(t, r);
|
||||
memcpy(mp.mpa.abtKey, t.sectors[i].KeyB, sizeof(t.sectors[i].KeyB));
|
||||
if (!nfc_initiator_mifare_cmd(r.pdi, MC_AUTH_B, block, &mp)) {
|
||||
// ERR ("Error: Auth B");
|
||||
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;
|
||||
}
|
||||
mf_configure(r.pdi);
|
||||
mf_anticollision(t, r);
|
||||
} else { // and Read
|
||||
if (nfc_initiator_mifare_cmd(r.pdi, MC_READ, block, &mp)) {
|
||||
if ((res = nfc_initiator_mifare_cmd(r.pdi, MC_READ, block, &mp)) >= 0) {
|
||||
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");
|
||||
|
@ -500,7 +728,8 @@ int main(int argc, char *const argv[])
|
|||
}
|
||||
|
||||
// Finally save all keys + data to file
|
||||
if (fwrite(&mtDump, 1, sizeof(mtDump), pfDump) != sizeof(mtDump)) {
|
||||
uint16_t dump_size = (t.num_blocks + 1) * 16;
|
||||
if (fwrite(&mtDump, 1, dump_size, pfDump) != dump_size) {
|
||||
fprintf(stdout, "Error, cannot write dump\n");
|
||||
fclose(pfDump);
|
||||
goto error;
|
||||
|
@ -527,20 +756,23 @@ error:
|
|||
|
||||
void usage(FILE *stream, int errno)
|
||||
{
|
||||
fprintf(stream, "Usage: mfoc [-h] [-k key]... [-P probnum] [-T tolerance] [-O output]\n");
|
||||
fprintf(stream, "Usage: mfoc [-h] [-k key] [-f file] ... [-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, " D number of distance probes, default is 20\n");
|
||||
// fprintf(stream, " S number of sets with keystreams, default is 5\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, " 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);
|
||||
|
@ -552,6 +784,10 @@ 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");
|
||||
|
@ -593,11 +829,6 @@ 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);
|
||||
|
@ -640,10 +871,6 @@ 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");
|
||||
|
@ -651,6 +878,48 @@ 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;
|
||||
|
@ -675,10 +944,11 @@ 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, m;
|
||||
int i;
|
||||
uint32_t m;
|
||||
|
||||
// Prepare AUTH command
|
||||
Auth[0] = (t.sectors[e_sector].foundKeyA) ? 0x60 : 0x61;
|
||||
Auth[0] = (t.sectors[e_sector].foundKeyA) ? MC_AUTH_A : MC_AUTH_B;
|
||||
iso14443a_crc_append(Auth, 2);
|
||||
// fprintf(stdout, "\nAuth command:\t");
|
||||
// print_hex(Auth, 4);
|
||||
|
@ -789,9 +1059,13 @@ 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 (true || ! 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++) {
|
||||
|
@ -824,7 +1098,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 ? 0x60 : 0x61;
|
||||
Auth[0] = dumpKeysA ? MC_AUTH_A : MC_AUTH_B;
|
||||
Auth[1] = a_sector;
|
||||
iso14443a_crc_append(Auth, 2);
|
||||
|
||||
|
|
44
src/mfoc.h
44
src/mfoc.h
|
@ -2,10 +2,24 @@
|
|||
#define TRY_KEYS 50
|
||||
|
||||
// Number of trailers == number of sectors
|
||||
// 16x64b = 16
|
||||
// Mifare Classic 1k 16x64b = 16
|
||||
#define NR_TRAILERS_1k (16)
|
||||
// 32x64b + 8*256b = 40
|
||||
// Mifare Classic Mini
|
||||
#define NR_TRAILERS_MINI (5)
|
||||
// Mifare Classic 4k 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
|
||||
|
||||
|
@ -16,7 +30,7 @@
|
|||
#define DEFAULT_DIST_NR 15
|
||||
|
||||
// Default number of probes for a key recovery for one sector
|
||||
#define DEFAULT_PROBES_NR 150
|
||||
#define DEFAULT_PROBES_NR 1
|
||||
|
||||
// Number of sets with 32b keys
|
||||
#define DEFAULT_SETS_NR 5
|
||||
|
@ -36,36 +50,35 @@ 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;
|
||||
bool b4K;
|
||||
uint32_t authuid;
|
||||
} mftag;
|
||||
|
||||
typedef struct {
|
||||
uint64_t *possibleKeys;
|
||||
uint32_t size;
|
||||
uint64_t *possibleKeys;
|
||||
uint32_t size;
|
||||
} pKeys;
|
||||
|
||||
typedef struct {
|
||||
uint64_t *brokenKeys;
|
||||
int32_t size;
|
||||
uint64_t *brokenKeys;
|
||||
uint32_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;
|
||||
|
||||
|
||||
|
@ -76,6 +89,7 @@ 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);
|
||||
|
|
68
src/mifare.c
68
src/mifare.c
|
@ -3,7 +3,7 @@
|
|||
*
|
||||
* Copyright (C) 2009 Roel Verdult
|
||||
* Copyright (C) 2010 Romain Tartière
|
||||
* Copyright (C) 2010, 2011 Romuald Conty
|
||||
* Copyright (C) 2010, 2011, 2013 Romuald Conty
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
|
@ -28,10 +28,23 @@
|
|||
* 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>
|
||||
|
@ -39,25 +52,33 @@
|
|||
#include <nfc/nfc.h>
|
||||
|
||||
/**
|
||||
* @brief Execute a MIFARE Classic Command
|
||||
* @return Returns true if action was successfully performed; otherwise returns false.
|
||||
* @brief Execute a MIFARE Classic command
|
||||
* @return Returns NFC_SUCCESS if action was successfully performed; otherwise returns error code (negative value).
|
||||
* @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).
|
||||
* The MIFARE Classic Specification (http://www.nxp.com/acrobat/other/identification/M001053_MF1ICS50_rev5_3.pdf) explains more about this process.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
bool
|
||||
int
|
||||
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)
|
||||
|
@ -89,7 +110,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 false;
|
||||
return NFC_EINVARG;
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -98,40 +119,23 @@ 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
|
||||
// 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;
|
||||
int res;
|
||||
if ((res = nfc_device_set_property_bool(pnd, NP_EASY_FRAMING, true)) < 0) {
|
||||
return res;
|
||||
}
|
||||
// Fire the mifare command
|
||||
int res;
|
||||
if ((res = nfc_initiator_transceive_bytes(pnd, abtCmd, 2 + szParamLen, abtRx, sizeof(abtRx), -1)) < 0) {
|
||||
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;
|
||||
return res;
|
||||
}
|
||||
/* 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 false;
|
||||
return NFC_EINVARG;
|
||||
}
|
||||
}
|
||||
// Command succesfully executed
|
||||
return true;
|
||||
return NFC_SUCCESS;
|
||||
}
|
||||
|
|
10
src/mifare.h
10
src/mifare.h
|
@ -3,7 +3,7 @@
|
|||
*
|
||||
* Copyright (C) 2009 Roel Verdult
|
||||
* Copyright (C) 2010 Romain Tartière
|
||||
* Copyright (C) 2010, 2011 Romuald Conty
|
||||
* Copyright (C) 2010, 2011, 2013 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()
|
||||
|
||||
bool nfc_initiator_mifare_cmd(nfc_device *pnd, const mifare_cmd mc, const uint8_t ui8Block, mifare_param *pmp);
|
||||
int 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)
|
||||
|
|
|
@ -1,9 +1,13 @@
|
|||
/*-
|
||||
* Public platform independent Near Field Communication (NFC) library examples
|
||||
* Free/Libre Near Field Communication (NFC) library
|
||||
*
|
||||
* Copyright (C) 2009 Roel Verdult
|
||||
* Copyright (C) 2010, 2011 Romain Tartière
|
||||
* Copyright (C) 2009, 2010, 2011, 2012 Romuald Conty
|
||||
* 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:
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
|
@ -115,10 +119,10 @@ print_hex_par(const uint8_t *pbtData, const size_t szBits, const uint8_t *pbtDat
|
|||
}
|
||||
|
||||
void
|
||||
print_nfc_target(const nfc_target nt, bool verbose)
|
||||
print_nfc_target(const nfc_target *pnt, bool verbose)
|
||||
{
|
||||
char *s;
|
||||
str_nfc_target(&s, nt, verbose);
|
||||
str_nfc_target(&s, pnt, verbose);
|
||||
printf("%s", s);
|
||||
free(s);
|
||||
nfc_free(s);
|
||||
}
|
||||
|
|
|
@ -1,9 +1,13 @@
|
|||
/*-
|
||||
* Public platform independent Near Field Communication (NFC) library examples
|
||||
* Free/Libre Near Field Communication (NFC) library
|
||||
*
|
||||
* Copyright (C) 2009 Roel Verdult
|
||||
* Copyright (C) 2010 Romain Tartière
|
||||
* Copyright (C) 2010, 2011, 2012 Romuald Conty
|
||||
* 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:
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
|
@ -94,6 +98,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 nt, bool verbose);
|
||||
void print_nfc_target(const nfc_target *pnt, bool verbose);
|
||||
|
||||
#endif
|
||||
|
|
437
src/slre.c
Normal file
437
src/slre.c
Normal file
|
@ -0,0 +1,437 @@
|
|||
/*
|
||||
* 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);
|
||||
}
|
60
src/slre.h
Normal file
60
src/slre.h
Normal file
|
@ -0,0 +1,60 @@
|
|||
/*
|
||||
* 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 */
|
Loading…
Reference in a new issue