How to implement the Fair Use Policy on a dev board?

'scuse me, but the AppKey doesnt get involved in the Join Request, if my reading of the LoRaWAN standard is correct. Two EUIs and nonce are shipped unencrypted, meaning that server can distinguish only those two with each nonce.
I get it’s better and safer that way, but it’s really cumbersome to have to keep track of the all those numbers, have a side application that generates new EUIs, or re-creates the device on the server side, instead of the usual flash-and-repeat.

Actually, it’s super anoying, since every flash rewrites the Flash content so I need to start with nonce=0, and I need to have the app that generates EUI both locally in my codebase and remotely on the server. On every single flash :face_with_head_bandage:

That number was pulled out as a satirical comment on my own incompetence.

Trust me, it is because it is used to encrypt the reply.

Doesn’t your controller/board have eeprom? That could be used to store the nonce. And for development purposes only you could check the command line tool TTN provides which might just have an option to reset the state of the device. (–resets-join-nonces comes to mind)

Love it! :+1: Guess I must have finally found someone even worse at this than me! :rofl:
Even I would draw a line at erm…4,999! :wink:

1 Like

Every few minutes is very much “frequently” in the context of LoRaWAN, as that’s the upper limit of how often you should be making any kind of transmission at all - transmissions which must be made within an ongoing join session.

The only time you’d be rejoining more frequently would be when you just flashed a development firmware and immediately realized that you hadn’t actually fixed the bug, or there was another one.

Typically one doesn’t field deploy the hardware instance used for development, but rather keeps that one around for further work and testing and deploys a fresh one. Even if you were going to deploy the same hardware instance it could be a fresh or reset registration instance. It shouldn’t need to rejoin at all in the field, but if there’s any way it could lose all state then having a node registration without any previous history from development decreases the chance that “nonce guessing” will waste battery and gateway airtime in already used values.

Also, they appkey is actually a part of the join request too, because it’s used in the initializer of the cryptographic checksum that ensures packet integrity (actually it uses almost all the mutually knowable details). Later on, the network session key is used, but for a join request that doesn’t exist yet.

At the end of the day if you want to do LoRaWAN correctly, then you need something like:

  • A high write life non-volatile memory where you can save state after every transmission, especially if your MCU cannot retain this information while in sleep mode. This appears to be what the LoRaWAN designers imagined people will do, but likely requires something like a magnetic RAM or at least a very large EEPROM to “walk” the records through.

  • A limited write life non-volatile memory where you save the join session and use a successive write “tally mark” like technique to track the uplink and downlink frame counters within the session, while walking them bit by bit through the memory to prevent wearing it out.

  • An MCU which does retain state, but using a limited write-life non-volatile memory for backup doing something like a battery change, where you save state every 1024 transmissions or something, and on cold-restart just skip the uplink frame count ahead by that.

  • An MCU which does retain state, but using a limited-write-life non-volatile memory to track the join nonces used, with the idea that in the rare case of losing power (battery change, etc) you then do a re-join.

One of the things that’s currently unfortunate is that the state saving callback in something like MCCI LMIC makes no attempt to separate the variables which do change constantly such as frame counters from the ones that change occasionally such as datarate, or theoretically like optional channels, etc vs the ones that don’t change within a session at all. So if you want to efficiently use limited write life memory, you need to write your own save and restore routine that’s more detail aware. As the code currently exists, its inefficient saving scheme can only be used for platforms that can save state in a moderate sized battery backed RAM, or in a high write life external memory.

5 Likes

Nope, not incompatibilities, versions less than 1.0.4 used a random number, v1.0.4 mandates an incrementing count.

Plus:

means, as Bad Cop moderator, I’ll re-asset:

For development purposes, almost all of my work is done with the radio off and I use serial debug to test things. But then I don’t download the firmware if I can help it as my life is too short, so I code-a-little, compile, test-a-little and when I think a function or module is ready, move it in to the firmware to do it for realz but not with the radio on. I really don’t find the need very often to actually send any payloads - that’s a known thing, I know what happens, the byte array gets moved from the device to the integration automagically. If I want to test the integration, I can copy & paste the serial debug byte array output to the simulate uplink console page and put it in there to see what happens in the other bits in my development control.

If I do need to test a radio setup - so for instance I find that there is stock of the LoRa-E5 in quantity and I resign myself to using that for the coming weeks - I may run the radio to check all is in order. But then I have options. First, I could just use ABP which frankly is still a spherical-ache for peeps to get right (not me, I have the command line secret sauce of making it happen - it’s only a secret if you can’t find it in a previous post) but then you have to master the whole resets frame counter “thing”. Or you can run OTAA and use the secret session reset button that’s hidden in plain sight on the site. Or as Jac suggests, use the CLI to reset. Or if you are the CLI Commander (that’s me), you use the ultra-useful “Ignore the JoinNonce” option - but I find I only have to use that with MLS on a WLR089U or SAMR34 board as it is pucka v1.0.4 which then does require the incrementing JoinNonce. Mostly on other code bases I just don’t join often enough to randomly hit the random number. And if I do I can wait for the next join. And of late, TTS has had the number it stores reduced as they had a lot of them hanging around for no apparent reason - the benefits of reading the release notes.

So as much as the interplay is a bit of fun, you will learn far more by doing stuff and asking questions when / if you get stuck than trying to debug the whole “not a real world scenario” before finding out what’s real.

And the wisdom when starting out is to use a first class supported board in the LMIC range - like the Adafruit Feather M0 with RFM95 - and not try to do things that aren’t implemented in LMIC which is complicated enough and just put the MCU to sleep where it will consume about 5uA and give you years of life on a set of AA batteries and save you the excitement of having to save state.

And for testing, just don’t run the radio - why sit and watch it for 10 seconds whilst it gets it’s thing done …

from the v1.0.4. spec:

The Join-Request frame contains the JoinEUI and DevEUI of the end-device followed by
a nonce of 2 octets (DevNonce). DevNonce is a counter starting at 0 …

So, no AppKey in the Join-Request.
To make your point, sure, the AppKey is used by the server to generate two session keys NwkSKey and AppSKey, but only after it receives the request packet. But to make my point, the server will only see those two EUIs and nonce and, based soley on that, determine if the device should be granted a join or not.

And thanks for pointing out the cli-option. Seems like it can make a life a lot easier.

As already stated by others, the AppKey is used to compute the MIC field of the JoinReq uplink frame (authentication operation).

In addition the AppKey is also used to decrypt the JoinAccept downlink and once decrypted the AppKey is again used to compute the JoinAccept MIC field in order to compare it with received MIC (authentication operation).

1 Like

yeah, got it. Thanks.