Nexus Keycode exceptions

  1. Is there any limit in entering previous nexus keycodes (Add Credit)?
  2. How many old keycodes should work in product?
  3. Do we need to save any Keycode id or count in NV memory ?
  4. Nexus Keycode rate limit function is activated when defined no.of invalid keycode entered.
  5. Here, what are those invalid keycodes (small keycode)? (a) Any invalid 15 digits keycode (b) Any invalid length digit keycode? (c) any valid keycode which are already used ?
  6. Do we need to save rate limit value in NV memory?

Anoop,

Thanks for registering on the Nexus forum! To answer your questions:

  1. Yes, if I interpret your question correctly, the Nexus library allows keycodes with message IDs from 23 before up to 40 above the highest-accepted message ID so far to be entered. We call this the “window” of acceptable keycode message IDs. The window is initialized with the highest ID as 23, so you can start by entering message IDs 0-63 (23-23=0; 23+40=63). If you then entered a keycode with message ID 10, then you could still enter 0-63 because the “highest ID” is 23. You could also enter a keycode with message ID 23 and not change the window. But if you entered a keycode with message ID 25, then the window would shift to accept keycodes with message IDs 2-65. A keycode with message ID 0, even if it was completely valid, would not update the credit on the device.
  2. Hope the above answers your question.
  3. No, this is handled by the PAYG NV.
  4. Nexus rate-limiting is activated when a Nexus library-tracked number of allowed token attempts is exceeded. By default the device allows 6 entries (CONFIG_NEXUS_KEYCODE_PROTOCOL_RATE_LIMIT_BUCKET_INITIAL_COUNT). Anytime the device processes a keycode, this number is decremented by 1. The number is incremented by 1 by default every 12 minutes (CONFIG_NEXUS_KEYCODE_PROTOCOL_RATE_LIMIT_REFILL_SECONDS_PER_ATTEMPT) until an upper limit of 128 (CONFIG_NEXUS_KEYCODE_PROTOCOL_RATE_LIMIT_BUCKET_MAX).
  5. For the purpose of rate-limiting, any 15-digit keycode counts regardless if it is totally invalid, valid, or valid and already used.
  6. No, this is handled by PAYG NV. You can change the configuration with the options mentioned above.

Thanks,

Eric

Hi Eric,

I have some specific questions about implementation of rate limiting feature:

I found below structure is used in rate limiting functions:

static struct
{
NEXUS_PACKED_STRUCT
{
uint8_t graceperiod_keycodes; // no.of Keycodes that can be entered before rate limiting
uint8_t pad[3]; // Padding byes
}
stored;
nexus_keycode_mas_message_handler handler; ??
struct nexus_keycode_frame partial; ??
bool max_length_exceeded; // Flag to check maximum keycodes - max - 30
uint32_t rl_bucket; // rate limiting bucket
} _this_core;

  1. Can you help me understand the variables?
  2. Which particular variable I need to same in NV memory?
  3. My code is not able to come out of rate limiting error, followed below steps:
    a) Entered 6 invalid keycodes to enter in rate limiting state.
    b) waited for more than 720 secs. (CONFIG_NEXUS_KEYCODE_PROTOCOL_RATE_LIMIT_REFILL_SECONDS_PER_ATTEMPT)
    c) Entered valid keycode to come out of rate limiting error, but it was again showing the same.
    d) while debug I observed the value of r1_bucket is not updating with new values.

Can you guide me on this?

Thanks in Advance
Anoop

Anoop,

First of all, I want to be clear that this struct is defined and used only in the src modules which are internal to the Nexus library; you should never need to use them in your code. Please confirm, otherwise I am worried that you will have bugs.

  1. That being said, I am happy to explain the variables in the struct:
static struct
{
NEXUS_PACKED_STRUCT
{
/* This is the initial count of keycodes in the rate-limiting bucket after
first programming and after reset if it had more than this count 
just before reset. If the device has fewer than this count 
in the bucket just before reset, then that count will be saved here 
and as the bucket increases up to
CONFIG_NEXUS_KEYCODE_PROTOCOL_RATE_LIMIT_BUCKET_INITIAL_COUNT
it will also save that count. */
uint8_t graceperiod_keycodes;
uint8_t pad[3]; // Padding byes
}
stored;
nexus_keycode_mas_message_handler handler; // this is an internal function
// that is used to process the keycode after you call
// `nx_keycode_handle_single_key`
// or `nx_keycode_handle_complete_keycode`
struct nexus_keycode_frame partial; // this is an internal struct
// that is used to keep track of a keycode during processing
bool max_length_exceeded; // this is used to check that the keycode
// is not too long
uint32_t rl_bucket; // current rate-limiting bucket count
} _this_core;
  1. You do not need to save any of this in your NV. It is saved in Nexus NV. You need to implement nxp_common_nv_write and nxp_common_nv_read so that the Nexus NV block can be saved and read. There are some examples in the docstrings of these functions. Only 8+16=24 bytes (block IDs 0 and 1) of NV are required to store all the Nexus NV data. Please tell me if this does not make sense.

  2. Thanks for the debugging info. Are you calling nx_common_process with your updated system uptime periodically? The rate-limiting bucket is updated when this function is called, so if you’re not calling it then Nexus is not aware of passing time.

Thanks,

Eric

Hi Eric,

  1. I will implement Nexus NV and will get back to you in case of doubts.
  2. nx_common_process function is being called every time as below:

// This function executes periodically from the main loop.
void processing_execute(void)
{
const uint32_t cur_uptime = clock_read_monotonic_time_seconds();
if (_this.nx_processing_requested)
{
_this.nx_processing_requested = false;
uint32_t max_secs_to_next_call = nx_common_process(cur_uptime);

// _this.its.it_value.tv_sec = max_secs_to_next_call;
// _this.its.it_value.tv_nsec = 0;
// _this.its.it_interval.tv_sec = 0;
// _this.its.it_interval.tv_nsec = 0;
// timer_settime(_this.timerid, 0, &_this.its, 0);
}

const uint32_t delta = cur_uptime - _this.last_uptime_seconds;
_this.last_uptime_seconds = cur_uptime;

_this.seconds_since_payg_state_backup += delta;

if (_this.seconds_since_payg_state_backup > ONE_HOUR_IN_SECONDS)
{
    payg_state_update_nv();
    _this.seconds_since_payg_state_backup = 0;
}

}

The above function is called every time in loop.
But still not able to come out of rate limiting error. Please guide what I am missing.

Thanks
Anoop

Hi Eric,

Just an update to my last posted question: I was not calling timer_thread() function, that was the reason I was not able to exit from rate limiting error.
No I have started a timer to call timer_thread function & rate limiting function is working now.
What should be the timer value in this case?? 20ms??

Thanks

Anoop,

Glad you figured out the problem.

In addition to calling nx_common_process when processing has been requested (via nxp_common_request_processing – this must be serviced within 20 ms!), you should also call it after max_secs_to_next_call has elapsed. I think this is probably what is missing.

The rate limiting bucket is incremented every 12 minutes, so it needs only a very low resolution timer. So I think it will be OK if you just call nx_common_process based on whether the Nexus Library processing is requested immediately or after max_secs_to_next_call has elapsed.

Eric

Hi Eric,

I need your help in implementation of Nexus - NV management:
As I understood, there are 2 types of data:

  1. Prod_nv Identity (20 bytes) -
    a. Serial ID- (size 4 bytes)
    b. Identity - (size 16 Bytes)
  2. Prod_nv_PaygState (6 bytes)-
    a. Credit Remaining ( size 4 bytes)
    b. is_unlocked (size 2 bytes)
  3. Common_nv
    a. Keycode_MAS (block id - 0, size - 8 bytes) - which stores graceperiod_keycodes? But the size of
    graceperiod keycode is uint8. Need clarity on extra 7 bytes ?
    b. Keycode_PRO (block id - 1, size - 16 bytes) - which stores _nexus_keycode_stored. Needs clarity on this as well?

Please comment if I have missed or interpreted wrong any data above?
I have to store the data in my Flash using these blocks only?
I can’t store data in my own way i.e by writing all data together at once?

Thanks
Anoop

Anoop,

You got the data blocks right. Below I’ll comment on the sizes.

1a. This should be 6 bytes for maximum portability. See nx_id.
1b. Correct, this is the nx_common_check_key.
2a. Correct.
2b. Correct – I assume that it is 2 bytes for alignment.

For 1 and 2, you can store them any way you like. This is your NV.

3a. Yes, this stores the graceperiod keycodes data which is 1 byte. 3 bytes are required for padding to make sure the data are word-aligned. Then the extra 4 bytes are required for the Nexus NV block wrapper which contains the length and ID of the block.
3b. Yes, you are correct about this. 12 bytes for the stored data plus 4 bytes for the wrapper.

You’ll notice that the signatures for nxp_common_nv_block functions have a nx_common_nv_block_meta argument. This is the 4-byte wrapper that you can use to go find the block in your NV, and the second argument is the buffer to write to or from.

I suggest to check out a sample implementation here: https://github.com/angaza/nexus-embedded/blob/master/nexus/examples/desktop_sample_program/src/nonvol.c

In this implementation all NV blocks are stored in the same file, but the Nexus NV blocks are marked with a special sentinel character. To write a new block, the program appends the new block with the sentinel, the Nexus NV block ID, and then the data itself. To read a specific block, the program seeks block-by-block until one with the Nexus NV sentinel is found. Then it checks the block ID. If it matches, it will save it and keep on looking until it finds the most recent block. It can do this because the new data is appended to the end of the file. If you want you can use nx_common_nv_block_valid to check if a block is valid before or after writing, but this is optional. We also suggest to keep a RAM copy of these data to prevent excessive writes in case the stored data is identical to the data that is requested to be written.

Of course based on your NV design your implementation will differ. For example if you store the Nexus NV in a region separate from other NV blocks, then you don’t need the sentinel. Or if you store only one copy of the data, you can simply overwrite a known location for each block. But be careful in case of corruption! We recommend to store at least an old and new copy.

Can you find out how to use these blocks in your NV architecture? You don’t have to use these blocks only; we assumed that you would have your own blocks and so made the blocks as small as possible so that they could be written alongside other blocks. If you have a lot of Flash lifetime, then of course you could write them all together at once whenever one changes.

Let me know if you still have questions!

Thanks,

Eric

Hi Eric!

I have implemented the NV part and is working well. Stored all the required blocks in Flash (NV) which are (1) Identity block (2) Payg State block (3) Keycode MAS block (4) Keycode PRO block.

My device is now able to retain the identity & credit etc. which verifies that Identity & Payg State blocks are stored & retrieved perfectly. How can I verify Keycode MAS & PRO blocks store & retrieve function?
Can you help me out in this?

Thanks

Anoop,

That’s great! First, did you try to use nx_common_nv_block_valid? This will check the block ID and CRC to ensure that the write/read from your NV did not get corrupted.

If you want to do some functional tests,

For MAS block, test keycode rate-limiting state

  1. Erase flash and reprogram device
  2. Enter invalid keycode
  3. Power-cycle device
  4. Repeat steps 2-3 five more times
  5. The device should be rate-limited and not allow any keys to be entered
  6. Wait 12 minutes and enter a keycode. If it is allowed, then MAS block is OK

For PRO block, test keycode entry

  1. Enter a valid keycode with any message ID
  2. Power-cycle the device
  3. Try to enter the same keycode. If nxp_keycode_feedback_start(NXP_KEYCODE_FEEDBACK_TYPE_MESSAGE_VALID) is called, then PRO block is OK.

It is of course possible to do more detailed tests but this is the basic validation. Since it sounds like you’re getting close to finishing, let me know if you’re interested in more complete test plans and I can put together some recommendations for deeper testing.

Eric

Hi Eric,
I am able to test my Flash implementation exactly as per you suggested :slight_smile:
It took some efforts to integrate the Keycode MAS & Keycode PRO blocks to my Flash, but finally its working well.
Now I want to know about the factory settings or Restore factory settings options.
case 1: What information we need to write during production? like secret key & serial number ?
case 2: Do we need to give feature to reset or modify these configurations (Secret key & serial number)? in case if the product need to allot to a new customer.

What is the procedure that we need to follow?

Thanks
Anoop

Anoop:

Case 1:

  • 6-byte Nexus ID (obtain this from Angaza Nexus API)
  • 16-byte secret key (obtain this from Angaza Nexus API)

Case 2:

  • The secrets above are assigned to one and only one device. They should never change after they are written. If the device needs to be sold to another customer, then the software platform (like Angaza Hub) will make the changes that allow the new customer to make payments with the same device ID.

Hi Eric,

One more question on Nexus Serial ID (6 Bytes): It has nothing to do with keycode decoding ? Can we use it as our product serial number?

Correct, it is not used with keycode decoding. The Nexus ID is guaranteed unique, so yes, it is suitable as a product serial number.

Eric

Hi Eric,

Thank you so much for your support.
I have started the product level testing now. Can you please guide, further steps to make product fully compatible with Angaza platform?

Also, I only integrated Nexus- Keycode functionality for now. Nexus-Channel will integrate in next phase of development (For which I may ask help) :slight_smile:

Thanks
Anoop

Hey Eric!
One observation during the keycode testing using small keycode:

  1. Generated 7 day token.
  2. Generated Unlock token
  3. Generated Set credit 0 token
  4. Entered 7 Day token & got the the 7 days credit
  5. Entered Unlock token & product On
  6. Entered Set credit 0 token-- Credit set to 0 & rate limit function also activated. (Not able to enter new token for 12 min)

is the step 5-6 is normal? please guide if I am doing something wrong.

Thanks

Anoop,

Regarding which tests to run, I recommend to follow this test plan that tests the Nexus Keycode minimum requirements. The requirements cover all the device transitions used on the Angaza Hub and also include some device hardware best practices.

Because you are able to generate your own keycodes, please ignore the references in the test plan to generating keycodes on the Angaza Hub. Also you do not have to record video of the tests, but in case you would like some debugging assistance or an expert to inspect your results and possibly detect a non-obvious bug, it is strongly recommended. I am happy to review the results with you.

As for the specific test that you ran and asked about, steps 5-6 are not expected if the device was just programmed and turned on. It should start with 6 keycodes in the rate-limiting bucket, and you only entered 3 keycodes. I would advise to completely erase the flash, reprogram, and try this test again.

Thanks,

Eric

Hi Eric,

I have started testing my device as per the Test plan you shared, I will share the results with you once done. Can I share same excel file with my results ??

There is 1 point which strikes through during my testing:

  1. Device has no credit.
  2. 1hr DQ token entered.
  3. Device has 1 hr credit.
  4. 2min DQ token entered?
  5. ??

What should be the outcome? should device accept 2min token or not?

Thanks

Anoop,

Yes, you can share the same excel file with your results.

What is the DQ token?

Eric