nfcpp is a libnfc wrapper library, which uses C++23 best practices.
- Header-only & Single-file. (Mostly)
- Commonly used operations are encapsulated.
- Exception-safety.
First, I must explain why this library is mostly a header-only single-file library.
- The library itself only contains
nfc.hpp - You still need to handle libnfc compilation/linking. - I'm just a wrapper.
- Because some header files are missing in the default libnfc installation,
nfc-extrais required. - If you require crypto functionality for Mifare Classic cards, you will also need the
crapto1library.
Important
Please continue reading for details on enabling crapto1 features.
For non-buildsystem projects.
- First, import libnfc in a reasonable way (details omitted).
- Please
git checkoutto the latest release. - Move files under the
srcdirectory, at least requiresnfc.hpp,nfc-extra/.
For non-xmake projects.
- TODO I will provide a reasonable import method for cmake/meson as soon as possible.
For xmake projects.
- TODO I wil submit this package to xrepo as soon as possible.
add_requires('nfcpp')
target('dummy')
add_packages('nfcpp')
...You only need to...
#include <nfc.hpp>
// It is recommended to use `using` to avoid overly long names, most classes in
// nfcpp begin with `Nfc`, so name conflicts are generally not a problem.
using namespace nfcpp;
using namespace nfcpp::mifare; // Provides `MifareCrypto1Cipher`.
using namespace nfcpp::util; // Some utilities may contain common names. (!)That's great! It will bring in everything you need.
The complete source code for the demo is here.
To avoid making the README too complicated, I have omitted many common things (such as include/using namespace/select_passive_targets...) and only kept what I thought was valuable.
A key feature of nfcpp is its support for transforming bitstreams (or bytestreams) to be transmitted at compile time (or runtime). This is supported by the NfcTransmitData template and several useful transformers is included by default; you will learn more in the documentation.
The following shows two commonly used transformers that add CRC or calculate parity for the input data. To avoid excessively long names, it is recommended to using them.
template <std::size_t N>
using data_parity = NfcTransmitDataAutoParity<N>;
template <std::size_t N>
using data_crc_parity = NfcTransmitDataAutoCRCParity<N, NfcCRC::ISO14443A>;Then, we will implement Mifare Classic Auth in pure software, with the card reader only transmitting the bitstream.
auto device = context.open_device();
auto initiator = device->as_initiator();
// The sector number and key to be authenticate.
constexpr auto SECTOR_ADDR = 0x00;
constexpr auto KEY_A = 0xFFFFFFFFFFFF;
// [R -> T] Request plaintext nonce. (Nt)
auto nt_r = initiator->transceive_bits(data_crc_parity(MC_AUTH_A, SECTOR_ADDR), buffer);
// [T -> R] Answer plaintext nonce.
// Each initializes its Crypto1 state.
auto nt = nt_r.as_big_endian().expect<uint32_t>();
MifareCrypto1Cipher cipher(KEY_A);
cipher.word(nuid ^ nt, false);
// [R -> T] Send reader answer. (Ar)
std::array<uint8_t, 4> nr{};
std::array<uint8_t, 4> ntt{};
nt = prng_successor(nt, 32);
for (auto i : std::views::iota(0, 4)) {
nt = prng_successor(nt, 8);
ntt[i] = nt & 0xff;
}
auto at_r = initiator->transceive_bits(
data_parity(nr, ntt).with_encrypt(
cipher,
[](auto&& cipher) {
cipher.crypt_feed(4);
cipher.crypt(4);
}
),
buffer
);
// [T -> R] Tag answer and reader verification.
// Authentication completed.
auto at = at_r.as_decrypted(cipher, false, false)
.as_big_endian()
.expect<uint32_t>();
nt = prng_successor(nt, 32);
if (at == nt) {
std::println("Authentication completed.");
}It's easy, isn't it? Only about 50 lines. Thanks to the power of nfcpp, you can enjoy the convenience without losing control over every bit, which is especially useful when writing RFID security tools.
The crypto1 implementation used by nfcpp comes from proxmark3, which is licensed under GPLv3.
To enable this feature, build nfcpp with following option.
xmake f --crapto1=true
xmake buildThis will automatically build the crapto1 library and enable all crypto1 features in nfc.hpp (via defines NFCPP_ENABLE_CRAPTO1=1).
Caution
With crapto1 enabled, this library is now subject to the GPLv3 license.
TODO
MIT by default, GPLv3 if crapto1 is used.