15 Sep 2026

feedPlanet Debian

Paul Tagliamonte: DESFire EV3

I've long been interested in hardware key material storage devices. I've been a fan of yubikeys (I still remember when my fancy new NEO-N showed up), PIV (and its associated smattering of additional fields), SaaS HSMs, the kernel keyring, some tooling I've fairly satisfied with the design of at prior companies, and of course, our dear friend, the TPM. All that is not even to mention the scores of exotic hardware security modules one generally comes across from time to time when you're keeping a sharp eye out that you wind up playing with.

The concept of storing private key material on a disk, or even having it in RAM has always skeeved me out, so I have a natural inclination to hardware modules, and how shifting keying material around can change your risks and threat model(s) in interesting ways.

I don't remember when I first came across the MIFARE DESFire EV3, but a few weeks ago I did a deep-dive into the state of the art of authentication schemes using ID cards. My complete overview of what tradeoffs exist is pretty extensive (and likely not interesting to the vast majority of the world), but the tl;dr wound up being one of "use PIV" or "use MIFARE DESFire EV3". I wound up picking DESFire for a recent project, and figured it's worth talking a bit about what I learned, share some thoughts, and some code. That code is published on crates.io/desox, and docs, as is our custom, may be found at docs.rs/desox

DESFire supports DES (I'm sure most readers saw that one coming), 3DES (I didn't bother playing with 3DES at all) or AES-128 (AFAICT always use this?) keying material. It's worth noting that the DESFire only supports symmetric keys and is not designed for public key cryptography, and operates exclusively using shared symmetric key material. The DESFire EV series use those keys and related authentication schemes to interact with "files" stored on the on-chip EEPROM (2k, 4k, 8k, and 16k versions exist), or "applications" (groups of files and authentication keys).

Talking to a DESFire EV3

Interactions with the card are done over NFC (ISO/IEC 14443 Type A), and commands to/from the card may be in the usual ISO/IEC 7816-4 APDU format, or "unencapsulated" bytes sent to/from the card are sent using a fixed instruction set and return code structure - saving a few bytes per message. I've opted to use their undocumented and proprietary format - I found it easier to work with and with a maximum message of 60 bytes, the savings matter a lot.

While powered via NFC, the card maintains a small amount of state about the connection between the reader and the card in its RAM, including if the session is authenticated or unauthenticated. I'll dig into how authentication happens later, but it's worth knowing that sessions can become authenticated using one of the symmetric keys shared by the card and the reader. The vast majority of the DESFire commands I know about tend to work while either authenticated or unauthenticated, with a few exceptions (GetUid, ChangeKey, and ChangeKeySettings for example).

In general, I found working with this card particularly pleasant. There is a fair amount of backwards-compatible behavior and multiple methods of communication that confuse things a bit, but overall, it was better than average to integrate with. Kudos to the NXP team. If the docs on this chip were public, things would be orders of magnitude easier - it's not entirely clear to my why they're keeping so much of the interface documentation under NDA, but it's the largest knock against the chip, by far.

Authentication

I found a lot of really great resources outlining how the handshake and protocol works for a DESFire EV3, especially from Ridrix, some public datasheets ThrRealRevK and posts from AndroidCrypto.

The gist here is that, because the DESFire only does symmetric key operations, the key exchange (a type of SKA - Symmetric Key Agreement) uses symmetric keys to establish a unique session key which is used to sign or encrypt data exchanged between the reader and the card. I'm not going to get too in-depth here, since there's a ton of other resources out there to dig into - but I will do a quick high-level description to keep this post mostly self-contained.

The authentication protocol serves two main functions - to verify that both parties know the same shared secret, as well as to act as a SKA to construct a new session shared secret key. Here's a quick overview of how a shared session key is derived between the reader and the card using our symmetric keys (AES-128 in the case below).

  1. the reader requests to start authentication with the card (something like AA 00 to start an AES Authentication handshake with keyslot 0x00).
  2. The card will then reply with AF (a status code that indicates more data is to follow), followed by 16 bytes (in the case of AES-128) of encrypted (using CBC) data.
  3. The hosts then decrypts this block with the symmetric key from keyslot 0, returning the card's session nonce.
  4. The host generates 16 bytes (usually random) for its session nonce.
  5. The host sends an instruction of AF (indicating a continuation of the previous command), followed by 32 bytes of encrypted data. When decrypted, the first 16 bytes are our nonce generated in step #4, followed by the 16 bytes provided by the card, decrypted in step #3, except where every byte is shifted to the left by one place (the 0th byte is copied to the end).
  6. The card will reply with 00 indicating a successful operation, followed by 16 bytes, which when decrypted, is our session nonce from step #4, shifted to the left by one byte in the same way that we did in step #5 with the card's nonce.
  7. At this point, both the reader and card have confirmed the other party has the same symmetric secret key. The session is now "authenticated" and a "session key" is derived using the two nonce blocks. Two hashing keys (K1 and K2) are derived from this key, which is used to maintain an ongoing CMAC hash of the messages coming and going to/from the card.

From here on out, the session is "authenticated", and responses from the card which were previously "plain" will now contain a 8-byte CMAC signature, which can be used to ensure that the replies in question come from the active session.

In my implementation of the handshake I opted to encode the handshake state into rust types, just so I wouldn't make any mistakes. The Handshake type contains the session internals (session nonce values, keying state, to include IV, etc). This means the authentication flow (from within my code) uses the Handshake struct to generate the commands to send to the card in order:

/// Create a new `Handshake`, and return the
/// start auth command (something like `AA 00`)
fn Handshake::<Initial>::begin(
 output: &mut [u8],
 key: [u8; 16],
 key_id: u8,
) -> (Self, &[u8]);

After we get a reply back from the card (the encrypted version of the card's session nonce, sometimes called Rnd_B in code I've seen), we transition states from Initial into HalfOpen.

/// Given the card's encrypted response, generate
/// our session nonce and generate a reply
/// (something that starts with `AF` followed by
/// 32 bytes of encrypted data).
fn Handshake::<Initial>::rnd_b(
 self,
 output: &mut [u8],
 input: &[u8]
) -> (Handshake::<HalfOpen>, &[u8]);

Now that we're "HalfOpen", we're waiting to hear back from the card to ensure that it, too, can byte-shift our provided nonce. Once we have the card's reply, we can check it using our complete helper, transitioning from HalfOpen to Successful.

/// Check to ensure that the card replied with
/// our nonce byte-shifted by one place, indicating
/// that they know the symmetric secret in
/// this key slot.
fn Handshake::<HalfOpen>::complete(
 self,
 input: &[u8]
) -> Handshake::<Successful>;

Once the Handshake is successful, the only thing left to do is consume the Handshake struct and turn it into the shared session key by running it through the key derivation function.

/// Consume the `Handshake` struct and return the
/// new shared session secret key.
fn Handshake::<Successful>::into_key(self) -> [u8; 16];

From here on out we can use this session key for the remainder of our interactions with the card - signing messages from (and sometimes to!) the card, or encrypted messages to and from the card. This key is used in CBC block mode, where the session IV is updated with the last block of the encrypted data.

Unit Testing

A nice proprietary of the SKA scheme we're using as part of DESFire is that the derived session key is actually deterministic if you control your nonce RNG (ok, actually, pretty true for most key agreements, but anyway), which means it is possible to capture traffic over the NFC interface, and "replay" the NFC I/O with cooked RNGs and ensure byte-identical messages and keys are generated. Within desox-rs this is called replay (I'm creative), and I've got a few replay sessions checked into VCS, which exercise a signficant amount fo the API surface. All were derived from an actual session with a real DESFire card, and can be updated with a live card and a --cfg flag.

Each replay file is a set of lines (request-response transactions), each containing two space-delimited hex encoded NFC messages. For instance, here's an authentication handshake in replay format:

1a00 afc7bbd82ff8fefae8
afc6dab54df2278d2952d560821be7e4c3 007d9abe94a9b14748

The code that generated that exchange came from the test stored adjacent to that file - a handshake with the default DES key (all zeros), and an RndA value hardcoded to 32c28fdafd3960de.

let mut card = card
 .authenticate_with_rnd_a(
 0x00,
 Key::Des([0; 8]),
 Key::Des(hex_literal::hex!("32 c2 8f da fd 39 60 de")),
 )
 .await
 .unwrap();

Since the card's RndB is similarly unchanging (I'm replaying this file every time), this will always derive the same session key, which means messages (including encrypted ones or CMAC signed responses) will be identical, as well. If you're playing with the DESFire yourself, feel free to grab my replay files if you need a "known good" baseline.

By default this will run using the MockBackend, replaying each file - expecting a byte-identical request, and responding with the harcoded customary reply. If the code (or test!) needs to change, updating the tests is done by swapping the MockBackend out for a real one. Since I had to do this a bunch during development, running cargo test with RUSTFLAGS="--cfg desox_replay_rw" will, on run, overwrite the replay file(s) for the executed test(s), ensuring all line-protocol changes are explicitly caught and reviewed.

Observations

Most commands, even ones which require authentication, are transmitted without CMAC signature(s) or encryption. CMAC signatures from the reader to the card are not really used (except for writes to a file which specifies communication must be CMAC signed), ditto for encryption (although that one is used for key change operations, in addition to file writes on files that specify encrypted communication must be used). The vast majority of commands take a "plain" request from the reader, and return a CMAC signed response.

By my eye, this means that a malicious reader, or something otherwise capable of holding the card online after communication with an authentic reader is complete are able to execute privilaged commands (since one can simply ignore the CMAC signatures on responses), so long as the command doesn't require the reader to provide CMAC signatures (or encryption), or allow the card to power down.

Fun with DESFire

I've played around a bit with ways to use the DESFire cards in interesting configurations, given what they're capable of. Here's some half-baked thoughts I had while mucking around with the cards - these are all poorly thought out sketches of some things we can do given the specific tradeoffs I see with the DESFire card. It's also worth noting that I don't have any of the actual documentation, and am not a cryptographic grown-up, so take these sketches with a massive grain of salt.

The first thing that came to mind when implementing this is how the authentication scheme can shift the boundary of what is and is not trusted (assuming good secure keying, and provided the key slots and card/application permissions are configured correctly). Rather than push the key material out to the machine connected to the NFC reader ("reader machine"), I instead tried turning the NFC reader and computer into something psuedo-untrusted by "merely" having it pass messages from the card to a trusted remote system ("remote machine"). This means that the "reader machine" is exchanging NFC data with the card, but that data is being decrypted, encrypted and processed by the trusted "remote machine" - the reader is unable to derive the session key.

For each of these, I wind up needing to authenticate - so there's still a few latent risks, but these can mostly be mitigated by asking for a readbacks of any changed file(s), setting key permissions carefully, and requesting the card's UID via the encrypted channel - all of which would require the symmetric secrets (which undermine the whole security model if comprimised).

This general construction is also subject to a hostile takeover of the untrusted "reader machine", since most commands (including destructive ones!) are sent in "PLAIN" mode - the reader machine can wait until authentication is complete and then inject commands into the card and "simply" ignore the CMAC signatures on responses, severing ties with the remote machine. As such, we also need to take steps to ensure that the key being used is not one that allows any access beyond what is allowed. Here were some ideas I sketched out off the back of this theory.

The "second-factor"

Given some established (and authenticated) connection, part of the initial authentication flow may use the DESFire card to prove physical control over it as part of a handshake. This can serve as a second factor during some authentication flow, requiring physical card presence at a reader to fully initialize a connection. This does have one glaring downside, however - it's phishable. To use this "for real", we'd need to take some steps to prevent obvious MITM flows (XOR the NFC messages with the URI as seen by the client?), but maybe there's something interesting there.

This also has a second interesting attribute - when used as part of a physical system authentication flow, this becomes a logical place to inject access control, being able to determine if some person is permitted to operate some device at that particular time (Is "Joe" current on his Laser Cutter certifications?) I think of the ideas I landed on, while conceptually interesting (using an employee id card as a 2FA token, it's very fast), this one is the least likely to turn into something real.

The "encrypted-cookie"

This construction, when paired with an encrypted DESFire file, allows the "remote machine" to read/write an 'encrypted cookie' to the card - storing small amount of encrypted data that the "remote machine" can read/write, but not the "reader machine", since this uses an encrypted and authenticated channel from the "remote machine" directly to the DESFire card, without any intermediate hosts needing to be fully trusted. I keep calling this the "encrypted cookie" in my head because it feels conceptually similar to how Ruby on Rails and Laravel handles cookies.

We'd need to take a few extra steps here (for instance, ensure that you read the cookie back over the encrypted channel after writing to prevent a malicious reader from dropping writes) to secure the system, but it feels like the structure of this is definitely decent.

The "takeover"

This time, let's say the computer attached to the NFC reader ("reader machine") is semi-trusted. For this scheme, our trusted "remote machine" and the "reader machine" pass messages over the network to handle authentication to the card (as above), where the handshake data is being decrypted, encrypted and processed by the trusted "remote machine" as usual. However, once the authentication handshake is complete and a session key has been derived, the "remote system" return the session key to the "reader machine", giving it a one-time-use key and authenticated session to the card.

We need to be careful about global/application permissions and key access control to files - but in this construction, we can allow the "reader machine" to take over privileged actions using a scope-limited DESFire key without handing over the card's true keying material (preventing cloning of the card). This can be helpful to ensure messages to/from the card are truely from the card (verifying CMAC signatures), enables the "reader machine" to directly read/write to/from encrypted file(s), but allows the symmetric key material to remain in as few places as possible - which is critical given compromising that secret will undermine the security of the entire system.

15 Sep 2026 1:25pm GMT

Yves-Alexis Perez: IKEv1 protocol disabled in strongSwan package for Debian unstable

Heads up, Debian IKE/IPsec users.

Starting with strongSwan 6.1.0-1 (currently in Debian unstable and targeted at Debian 14 Forky), the IKEv1 protocol has been disabled. This is aligned with upstream decision. Considering IKEv2 is already nearly old enough to drink in the USA (RFC 4306 will turn 21 next December) and IKEv1 has weaknesses, the disabling is long overdue amd will permit upstream to remove some code in the upcoming years.

At this point there is no good reason not to migrate to IKEv2 and exposing IKEv1 code in all Debian installation is no longer relevant. All IKEv1 users using Debian 13 Trixie (either site to site, gateway or roadwarrior client) should investigate IKEv2 protocol (or other options).

Note that some plugins have also been disabled upstream for security/maintenance reasons and we followed suite in Debian. The Debian relevant ones are: af-alg, led, padlock.

15 Sep 2026 7:28am GMT

Reproducible Builds: Supporter spotlight: Jochen Sprickerhof on ... Reproducible Builds!

The Reproducible Builds project relies on several projects, supporters and sponsors for financial support, but they are also valued as ambassadors who spread the word about our project and the work that we do.

This is the ninth installment in a series featuring the projects, companies and individuals who support the Reproducible Builds project. We started this series by featuring the Civil Infrastructure Platform project, and followed this up with a post about the Ford Foundation as well as recent ones about ARDC, the Google Open Source Security Team (GOSST), Bootstrappable Builds, the F-Droid project, David A. Wheeler, Simon Butler and Kees Cook.

Today, however, we will be talking with Jochen Sprickerhof, one of the newer members of the Reproducible Builds project core team.



Vagrant Cascadian: Could you tell me a bit about yourself? What sort of things do you work on?

Jochen Sprickerhof: I am a freelance programmer working on Open Source. Mainly doing Debian, F-Droid and some smaller software projects. In general I made it a habit to look into every software I use and try to fix bugs or add features I need. In Debian, I maintain about 180 packages with topics covering home banking, build systems and robotics. Most of my time, I currently work on reproduce.debian.net, where we try to bit-for-bit reproduce the packages distributed by Debian.


Vagrant: Could you describe the path that lead you to working on reproducible builds?

Jochen: I started my Debian journey as a teenager, converting my school to Debian and serving as its system administrator for 13 years. After studying Applied System Science, I joined the university's robotics labs, where I worked on the Robot Operating System (ROS) and the Point Cloud Library (PCL). In the end, I enjoyed programming more than writing papers, so I eventually left academia for a robotics startup. Some years ago, I realized that the open source work I was doing in my spare time was actually the work I cared most about. Nowadays I am really grateful that I can spend my days working on things I find important and have lots of fun with.


Vagrant: What projects did you recently make big progress on?

Jochen: A recent example is metasnap.debian.net. It is a 'meta archive' of snapshot.debian.org which is itself archive of all packages in Debian. But let me explain it the other way round: with reproduce.debian.net, we try to reproduce the packages as they are distributed by the Debian archive. For that, we need the same build environment (compilers, libraries, build tools, etc) that was used by Debian back when the original package was compiled. Luckily, snapshot.debian.org has all those packages, but they are not easily accessible via apt, Debian's package manager. So, metasnap provides a mapping from a package name and version pair to the APT repo on snapshot.debian.org needed to download it from. It was created by josch some time ago, and it's awesome work. But when we tried to reproduce more and more packages on reproduce.debian.net, we found that some were missing packages from the build environment - even though they where visible on snapshot.debian.org. We found that metasnap excluded some archive areas because they where not expected to be needed. Reimporting all the data took more than two months and surfaced a couple more flaws.

With this fixed, we were able to build more packages, only to find out that metasnap also needs better support for version numbers. Luckily we were able to rewrite the data in a day instead of starting the import again.


Vagrant: You have been working on infrastructure to support reproducible builds for a while. Has recent adoption of reproduce.debian.net into the Debian release tooling changed the focus of your work?

Jochen: Quite a bit. When we started reproduce.debian.net in 2024, only around 33% of the packages could be reproduced successfully. Today we are above 98%. Most were not bugs in the packages themselves but in the infrastructure. Similar to the metasnap issue I reference above, packages just needed a rebuild because something else, like the toolchain, was fixed in the meantime. In May, people from the Debian release team and the Reproducible Builds project sat together and decided that the overall state is good enough, and now packages that regress on reproducibility are blocked from entering the next Debian release. But that does not mean all the work is on the shoulders of Debian package maintainers. Since then I have been constantly looking at the migration tooling to spot regressions and provide fixes. Furthermore, a couple of maintainers reached out to us for help and I hope more will do so in future.


Vagrant: What is one small thing you (or others) have not yet gotten to that you would really like to see?

Jochen: The central tool to reproduce Debian packages is debrebuild, also written by josch. Currently it has two ways to retrieve the build dependencies of a package. Either it uses metasnap.debian.net (as explained above), or it can access the Debian unstable APT repository directly. This allows to test packages locally before everything is indexed on metasnap by compiling against Debian unstable. But actually there are many other APT repositories to query, like Debian stable or even derivatives. Adding support for an optional list of APT repositories in debrebuild would be great. That would also be a big step to support reproducing other Debian based distributions.


Vagrant: … and one big thing?

Jochen: It would be great to integrate metasnap.debian.net into snapshot.debian.org. There is some discussion on it already in Debian bug #650783.


Vagrant: What are the tools you use the most?

Jochen: According to my fish shell history:

$ history | cut -d' ' -f1 | sort | uniq -c | sort -nr | head -10
  36199 git
  20941 vi
  12271 rm
   8599 cd
   7917 ls
   6407 apt
   5631 grep
   4249 mv
   3655 dpkg
   2873 cp


Vagrant: So, is the fish shell reproducible? I remember it did not used to be…

Jochen: You can check for yourself - it was last time I checked. But looking through the other commands, neovim sadly is not. I hope we can fix that in future.


Vagrant: Oh, that's a nice URL to check for reproducible package… you can just pass the source package name to check the current results?

Jochen: Yes. Another one is udd.debian.org/reproducibility/, where you can list all packages of a Debian maintainer. It also lists source reproducibility and has nice filters as well.


Vagrant: What tools do you use specifically working on reproducible builds?

Jochen: I don't have statistics for that, but I would say sbuild to build the package, debrebuild to reproduce it, and diffoscope to analyze the differences. Obviously I also need run apt source <package> or use git-buildpackage to get the sources and all the tools I mentioned above.


Vagrant: So how many packages are left to build reproducibly, and once those are finished, what is next?

Jochen: Right now, reproduce.debian.net shows over 98% reproducibility, though there are still over 650 package left and some will probably need a lot of work. But actually I think making packages reproducible is just the first step. For me, this is a project to build confidence in the system. To reproduce a package we have two parts: the source of the package and the build environment. Fixing the packages means gaining confidence in the first part but we still rely on the individual build environments for each package as we need to use the same compiler that was used when the package was build initially. Because of this, we have to keep around every historical version of all toolchain packages. I really would like to remove this extra archive, which means we would have to rebuild all of Debian around release time. I am dreaming of a Debian release where you could bit-for-bit reproduce every package just from the released versions. Due to how Debian works, however, this is not a trivial rebuild and it would need some work on the infrastructure. By the way, initially there was a third component to pay attention to: any connection to the outside world during the build. Luckily we fixed the Debian build daemons to not allow network connections during the build some time ago.


Vagrant: Thanks for all that work, and taking the time to tell us a bit about yourself!

Jochen: Thanks a lot for the interview!




For more information about the Reproducible Builds project, please see our website at reproducible-builds.org. If you are interested in ensuring the ongoing security of the software that underpins our civilisation and wish to sponsor the Reproducible Builds project, please reach out to the project by emailing contact@reproducible-builds.org.

15 Sep 2026 3:54am GMT