WSJT-X SuperFox Verification is flawed

WSJT-X has published a release candidate which includes a new fox mode called SuperFox which promises a +10dB total system gain compared to the old fox mode. It also comes with a “SuperFox digital signature” in attempt to alleviate dx-pedition pirates. Verification of dx-peditions is an excellent idea, and I really want to see this problem be solved.

A screenshot of WSJT-X showing a VK callsign being used in SuperFox mode - freetext says “OC-001 Ultrarare DxPed”

There’s a few problems with the SuperFox signature system, however. Let’s talk about it.

How does SuperFox signatures work?

The basic flow is this:

  1. Before a dx-pedition, the group running the dx-pedition applies to Northern California DX Foundation to receive a “SuperFox key”
  2. During the dx-pedition, the fox transmitter configures the “SuperFox key” into their WSJT-X application
  3. When transmitting in SuperFox mode, WSJT-X will encode a verification signature into each message
  4. Hounds will receive this verification signature and ensure its valid
  5. If the message is valid, a “Verified” message is displayed in WSJT-X

Governance issues

The first problem to dive into is how the dx-peditions keys are distributed. We are reliant to a single org to distribute keys for all dx-peditions world wide. This can pose issues if the org doesn’t accept your dx-peditions credentials or is uncontactable for some reason.

Given the global reach of this hobby it’s frustrating to see this approach taken.

What could be done better

Public/private keys could be generated by any dx-peditions and used within WSJT-X. The public key could be uploaded to the dx-pedition website where users could check for validity. This allows anyone to use the new SuperFox mode. WSJT-X might want to have a certificate download system where wsjt-x could download certificates automatically that have been vetted.

GPL source code and amateur radio ethos

SuperFox decoders and the signature check are packaged as binary blobs and not covered by the GPL. This means that users installing through open source channels will not be able to use the SuperFox mode as the binary blobs need to be stripped from the packages.

Additionally, the use of binary blobs may not be in line with the project’s GPL licence, but I’ll leave that debate up to the legal people.

Regardless of the legal aspects, I’m a strong believer that amateur radio should focus on using open standards, protocols and modulation schemes where possible, and this very much goes against that. Keeping the modulation and verification scheme a secret prevents innovation and limits the lifetime of the service.

What could be done better

Develop the digital signature process in public with open source code. This allows feedback and improvements. It also ensures that the project can live on.

Security by obscurity is a bad idea

I’m sure you already know this one. Security by hiding the algorithm is a bad idea. You might have noticed that when I walked through how SuperFox is meant to work, that there was no step for where “WSJT-X downloads latest keys” or “user inserts public key”. That’s because the only security provided by this system is from the algorithm used to “sign” the messages.

The good news is that it’s no longer obscure…

mwheeler@foxbook superfox_keygen % gcc -I. main.c <censored>.c
mwheeler@foxbook superfox_keygen % ./a.out N0CALL
OP0C-COPY

I spent a little bit of time looking at how the binaries worked and made my own implementation of the key generator.

A public release of this code as GPL will be available after the Jarvis Island 2024 dx-pedition.

What could be done better

Including some people knowledgable in secure system design would be a great start. This system was very hand crafted without much experience and didn’t use any existing open standards for signing messages. Being amateur radio, we don’t need anything fancy. In fact, H40WA implemented a reasonably workable solution using TOTP tokens to verify that the station wasn’t a pirate. Alternatively, some basic public/private key cryptography could have helped here.

Oh no.

So what’s going on here. (note: I’m no cryptography expert)

The major problem with SuperFox is the system design for the digital “signature”. The system is symmetrical which means the receiver needs to know how the key and the process used to “sign” the message. This means the only thing protecting someone from generating a SuperFox key is client side security - a terrible place to be in.

Facepalming xssfox

In a future post after the Jarvis Island 2024 dx-pedition I’ll write up the steps I took in discovering how SuperFox and foxchk work. This work was done with the intention of creating open source versions of the closed binaries to allow SuperFox to work on Debian, however as the security issues were discovered it was important to document the issues publicly (while keeping the details private for now) to allow developers a chance to hopefully change their approach to something more open and sustainable.

With help/love from

  • the6p4c
  • isomer
  • kitty
  • my insomnia

CODAN Selcall Part 1

We drive long distances in remote places. Sometimes we want people to be able to check-in on us while we are out and about. While cell coverage is getting better we still often find ourselves in places with no coverage.

Our 4x4 with large HF antennas mounted driving through a dry lake

Often we leave our HF radio tuned to a known frequency (usually 7045 kHz LSB) while we are driving and have VSC or power level squelch enabled. However this has false triggers. When driving through towns there is usually a lot of RF noise and even when we aren’t there’s noise from our car, other HF radios, or even just ionsondes or CW operators.

Random noises while driving are very frustrating. In the commercial space radios are fitted with selective calling (Selcall) functionality. This allows one radio to call another radio provided you know the ID number of the other radio.

The great thing about standards is that there are so many to chose from - Selcall being no exception. While there are many different Selcall variations today we are going to talk about the Codan specific Selcall - specifically the not the CCIR / UN / WA2 / RDD / Customs variants. Nor aircraft SelCal. The proprietary Codan “standard” is what we want to look at. The reason behind this is that there are numerous Selcall users on 7045kHz already using Codan Selcall and while it’s proprietary we can still interoperate with those users.

Modulating

The actual Codan Selcall process is actually pretty well documented with VK5QI already creating an open modulator including some of the less documented features such as channel test modes.

I decided to use freedvtnc2 as a skeleton project as it already has an FSK modem (Codec2), audio and rigctl components. From there I implemented the Selcall protocol while testing and researching some of the extra modes from our Codan 9323. Modulating a Selcall signal was now trivial - but we already knew and had that. What we really wanted was an opensource decoder. Something that could detect Selcalls.

Demodulating

Demodulating is a little bit harder, but given that we know what the signal looks like and Codec2 provides a good FSK demodulator it was not outside my skill level.

Codan Selcalls start with a preamble, usually quite long to allow radios scanning channels enough time to detect the signal. To an extent we can ignore the preamble as the Codec2 modem will handle locking onto the signal for us.

After the preamble there is a phasing pattern. This is a header that is at the start of every Codan Selcall. It’s nearly as simple as looking for this pattern to determine when a Selcall has started. But you don’t want to do an exact match as in HF radio its likely that noise has caused some bits to flip. I settled for a 85% threshold, however I think it could be even lower in practice.

Once the phasing pattern is found it’s matter of decoding the bits as they come in. There’s a few fun things in here. Codan uses a 7 bit word and 3 bits of parity, so you have to handle the incoming data as a series of bits rather than your typical bytes. Parity is only at the word level. There is no checksum on the entire message. The data is effectively sent twice (offset) so if parity fails on one word you might be able to recover it from the other.

The parity is a count of the number of 0’s in the word.

For my demodulator it works like this:

  • Store a rolling buffer long enough to fit a Selcall
  • Look for phasing pattern
  • If a phasing pattern is found try to decode each field - check parity - use the redundant field if required and available

Fun fact - if you are really unlucky both copies of a field could return a successful parity check but have different data - as there is no overall checksum for the message you won’t know which one is correct. When receiving a Selcall we just check if either field is our Selcall ID.

So you decoded a Selcall - what now?

Decoding the Selcall is only part of the process. We need a way of alerting the user that there’s a call for them. It would be really really really nice if ICOM allowed user defined messages (more on this later) and activating a beeper - but in the meantime lets aim for something simple. Rigctl.

When we send a Selcall we actually need to control the rig - turn the radio to data mode, trigger ptt, return the radio to voice mode. Since we already have rigctl for that, how about we use it for alerting the use as well.

The simplest solution I thought of is using the squelch adjust. On our rig you can configure the USB audio output to remain active even when the radio is squelched on the head unit. So the flow looks like this now.

  1. Squelch the radio to a fairly high level - unlikely to get false triggers
  2. Software listens for the Selcall
  3. When it receives a Selcall for our configured ID number use rigctl to unsquelch the radio
  4. (additionally) if the Selcall requested the channel test mode and the option is enabled send back a test signal

Freeselcall

I built all of this over a couple of days and have called it freeselcall. It’s had some over the air tests - but I wouldn’t say it’s battle hardened.

I realised that most people, myself included, wouldn’t want to use a terminal to perform selcalls so I’ve also added a web interface. It uses websockets so it should be easy to integrate with other systems as well.

Freeselcall web interface
(It even has browser notifications for those so inclined)

More than just Selcall

Freeselcall allows for sending and receiving Selcalls with various priority types along with the option for channel tests. However Codan radios can do more. They can do paging (short messages).

Today freeselcall can receive pages. It also has code to send pages however Codan decided to add some security words to process. As it stands a Codan radio will not receive the page messages from freeselcall.

Terminal prompt showing Codan 9323 boot up logo and some debug output

I’ve spent a lot of time in Ghidra along with building a Codan 9323 emulator (it can display the LCD boot up message!) to help reverse engineer how these security bytes work. Hopefully in part 2 I can share how the paging messages work. Emulating a unique flavour of 8051 along with the hardware it connects to has been fun.

The future (and a terrible idea)

As I alluded to earlier - being able to display a message on our ICOM 7100 would be ideal. However there’s no rigctl or CI-V way of displaying a message on the 7100. Or so I thought. It was only when I was dialing in a repeater on the 7100 that I came across an idea that might work. I haven’t tested this, but here’s the plan.

  1. Dedicate a memory channel for freeselcall.
  2. When a call comes, if the radio is in memory mode, store what memory the radio is in.
  3. Use CI-V command to save the current radio state as a memory in the predefined freeselcall memory channel overwriting any existing configuration in that slot.
  4. Update the channel name with the message / selcall id / ect….
  5. Tell the radio to load that memory
  6. After some time has passed and the radio hasn’t PTT’d in awhile, either restore the radio to the memory it was set to or turn off memory mode

I think this will work? But I have no idea until I try

The other addition I want to add is either implementing more sensible Selcall protocols or building a more modern robust one. This can run in parallel for backwards compatibility.

I haven’t even looked into 6 digit pagecall. I haven’t seen it used on amateur bands so I’m not overly interested.

A final note….

Part 2 won’t be coming out until I’ve either been able to emulate the Codan firmware (just because thats cool in its own right) or I’ve been able to figure out the magic for the secret words. The emulator is at the point where I need to have the base and the head unit emulated at the same time and talk via the I2C bus. This shouldn’t be hard, however it’s also not something that is trivial.

Please do not send me yet another a copy of the Codan CCIR 493-4 PDF. This afaik doesn’t contain any information on the secret words and I have enough copies of it from people being helpful.