ra8-firmware 0.1.0
Bare-metal firmware for the Renesas RA8 family (RA8D2 / RA8P1)
Loading...
Searching...
No Matches
ra8_net_provision.c
Go to the documentation of this file.
1
17
18#include "ra8_net_provision.h"
19
20#include <stddef.h>
21#include <stdint.h>
22
23#include "ra8_attributes.h"
24#include "ra8_err.h"
25#include "ra8_secure.h"
26
29 'R',
30 'A',
31 '8',
32 'N',
33 'E',
34 'T',
35 '1',
36 ':',
37};
38
58
76
77const char k_ra8_net_provision_ready_prompt[] = "ra8_net_provision: READY v1\r\n";
78
94RA8_INTERNAL static bool internal_hex_nibble(uint8_t digit, uint8_t* out)
95{
96 *out = 0U;
97 if ((digit >= (uint8_t)'0') && (digit <= (uint8_t)'9')) {
98 *out = (uint8_t)(digit - (uint8_t)'0');
99 return true;
100 }
101 if ((digit >= (uint8_t)'a') && (digit <= (uint8_t)'f')) {
102 *out = (uint8_t)(digit - (uint8_t)'a' + (uint8_t)k_ra8_net_provision_hex_alpha_offset);
103 return true;
104 }
105 if ((digit >= (uint8_t)'A') && (digit <= (uint8_t)'F')) {
106 *out = (uint8_t)(digit - (uint8_t)'A' + (uint8_t)k_ra8_net_provision_hex_alpha_offset);
107 return true;
108 }
109 return false;
110}
111
134 size_t begin,
135 size_t end,
136 char* output,
137 size_t capacity,
138 size_t* out_length)
139{
140 const size_t encoded = end - begin;
141 if ((encoded % 2U) != 0U) {
143 }
144 const size_t decoded = encoded / 2U;
145 if (decoded > capacity) {
147 }
148 for (size_t index = 0U; index < decoded; ++index) {
149 uint8_t high = 0U;
150 uint8_t low = 0U;
151 if (!internal_hex_nibble(line[begin + (index * 2U)], &high)) {
153 }
154 if (!internal_hex_nibble(line[begin + (index * 2U) + 1U], &low)) {
156 }
157 output[index] = (char)((uint8_t)(high << 4U) | low);
158 }
159 output[decoded] = '\0';
160 *out_length = decoded;
161 return k_ra8_ok;
162}
163
180RA8_INTERNAL static bool internal_has_no_controls(const char* text, size_t length)
181{
182 for (size_t index = 0U; index < length; ++index) {
183 const uint8_t byte = (uint8_t)text[index];
184 if ((byte < (uint8_t)k_ra8_net_provision_text_printable_min) ||
185 (byte == (uint8_t)k_ra8_net_provision_text_delete)) {
186 return false;
187 }
188 }
189 return true;
190}
191
207RA8_INTERNAL static bool internal_psk_is_hex(const char* psk)
208{
209 for (size_t index = 0U; index < (size_t)k_ra8_net_provision_psk_bytes_max; ++index) {
210 uint8_t ignored = 0U;
211 if (!internal_hex_nibble((uint8_t)psk[index], &ignored)) {
212 return false;
213 }
214 }
215 return true;
216}
217
236{
237 if (candidate->ssid_len == 0U) {
239 }
240 if (candidate->psk_len < 8U) {
242 }
243 if (!internal_has_no_controls(candidate->ssid, candidate->ssid_len)) {
245 }
246 if (!internal_has_no_controls(candidate->psk, candidate->psk_len)) {
248 }
249 if (!internal_has_no_controls(candidate->url, candidate->url_len)) {
251 }
252 if ((candidate->psk_len == (uint8_t)k_ra8_net_provision_psk_bytes_max) &&
253 !internal_psk_is_hex(candidate->psk)) {
255 }
256 return k_ra8_ok;
257}
258
278internal_decode_line(const uint8_t* line, size_t line_length, ra8_net_credentials_t* candidate)
279{
280 size_t separators[2] = {};
281 uint8_t separator_count = 0U;
282 for (size_t index = (size_t)k_ra8_net_provision_prefix_bytes; index < (line_length - 1U);
283 ++index) {
284 if (line[index] == (uint8_t)':') {
285 if (separator_count >= 2U) {
287 }
288 separators[separator_count] = index;
289 separator_count++;
290 }
291 }
292 if (separator_count != 2U) {
294 }
295
296 size_t decoded_length = 0U;
299 separators[0],
300 candidate->ssid,
302 &decoded_length);
303 candidate->ssid_len = (uint8_t)decoded_length;
304 if (err == k_ra8_ok) {
305 err = internal_decode_field(line,
306 separators[0] + 1U,
307 separators[1],
308 candidate->psk,
310 &decoded_length);
311 candidate->psk_len = (uint8_t)decoded_length;
312 }
313 if (err == k_ra8_ok) {
314 err = internal_decode_field(line,
315 separators[1] + 1U,
316 line_length - 1U,
317 candidate->url,
319 &decoded_length);
320 candidate->url_len = (uint16_t)decoded_length;
321 }
322 return (err == k_ra8_ok) ? internal_validate_candidate(candidate) : err;
323}
324
340RA8_INTERNAL static bool internal_prefix_matches(const uint8_t* line)
341{
342 for (size_t index = 0U; index < (size_t)k_ra8_net_provision_prefix_bytes; ++index) {
343 if (line[index] != s_prefix[index]) {
344 return false;
345 }
346 }
347 return true;
348}
349
351{
352 if (credentials != nullptr) {
353 ra8_secure_memzero(credentials, sizeof(*credentials));
354 }
355}
356
358ra8_net_provision_parse(const uint8_t* line, size_t line_length, ra8_net_credentials_t* out)
359{
360 if (out == nullptr) {
361 return k_ra8_err_null_ptr;
362 }
364 if (line == nullptr) {
365 return k_ra8_err_null_ptr;
366 }
367 if ((line_length <= (size_t)k_ra8_net_provision_prefix_bytes) ||
368 (line_length > (size_t)k_ra8_net_provision_line_bytes_max)) {
370 }
371 if (!internal_prefix_matches(line)) {
373 }
374 if (line[line_length - 1U] != (uint8_t)'\n') {
376 }
377
378 ra8_net_credentials_t candidate = {};
379 const ra8_err_t err = internal_decode_line(line, line_length, &candidate);
380 if (err == k_ra8_ok) {
381 *out = candidate;
382 }
383 ra8_net_provision_clear(&candidate);
384 return err;
385}
386
405 uint32_t timeout_ms)
406{
407 if (uart == nullptr) {
408 return k_ra8_err_null_ptr;
409 }
410 if (uart->write == nullptr) {
411 return k_ra8_err_null_ptr;
412 }
413 if (uart->read == nullptr) {
414 return k_ra8_err_null_ptr;
415 }
416 if (uart->wait_ms == nullptr) {
417 return k_ra8_err_null_ptr;
418 }
419 if ((timeout_ms == 0U) || (timeout_ms > (uint32_t)k_ra8_net_provision_timeout_ms_max)) {
421 }
422 return k_ra8_ok;
423}
424
442RA8_INTERNAL static bool
443internal_find_newline(const uint8_t* chunk, size_t length, size_t* newline_offset)
444{
445 *newline_offset = 0U;
446 for (size_t index = 0U; index < length; ++index) {
447 if (chunk[index] == (uint8_t)'\n') {
448 *newline_offset = index;
449 return true;
450 }
451 }
452 return false;
453}
454
478 uint32_t timeout_ms,
479 uint8_t* line,
480 size_t line_capacity,
481 size_t* line_length)
482{
483 ra8_err_t err = k_ra8_ok;
484 size_t used = 0U;
485 bool complete = false;
486 for (uint32_t elapsed = 0U; elapsed < timeout_ms; ++elapsed) {
487 size_t received = 0U;
488 const size_t remaining = line_capacity - used;
489 err = uart->read(&line[used], remaining, &received);
490 if (err != k_ra8_ok) {
491 break;
492 }
493 if (received > remaining) {
495 break;
496 }
497 size_t newline_offset = 0U;
498 if (internal_find_newline(&line[used], received, &newline_offset)) {
499 used += newline_offset + 1U;
500 if ((newline_offset + 1U) != received) {
502 }
503 complete = true;
504 break;
505 }
506 used += received;
507 if (used == line_capacity) {
509 break;
510 }
511 uart->wait_ms(1U);
512 }
513 if ((err == k_ra8_ok) && !complete) {
514 err = k_ra8_err_timeout;
515 }
516 *line_length = used;
517 return err;
518}
519
521 uint32_t timeout_ms,
523{
524 if (out == nullptr) {
525 return k_ra8_err_null_ptr;
526 }
528 ra8_err_t err = internal_validate_receiver(uart, timeout_ms);
529 if (err != k_ra8_ok) {
530 return err;
531 }
532
533 const size_t prompt_length = sizeof(k_ra8_net_provision_ready_prompt) - 1U;
534 err = uart->write((const uint8_t*)k_ra8_net_provision_ready_prompt, prompt_length);
535 uint8_t line[k_ra8_net_provision_line_bytes_max] = {};
536 size_t used = 0U;
537 if (err == k_ra8_ok) {
538 err = internal_receive_line(uart, timeout_ms, line, sizeof(line), &used);
539 }
540 if (err == k_ra8_ok) {
541 err = ra8_net_provision_parse(line, used, out);
542 }
543 ra8_secure_memzero(line, sizeof(line));
544 if (err != k_ra8_ok) {
546 }
547 return err;
548}
Annotation-attribute framework macros for ra8-firmware.
#define RA8_INTERNAL
Marker that a function is intended to be static (file-local).
Error Code Definitions for ra8-firmware.
@ k_ra8_err_invalid_arg
Invalid function argument.
Definition ra8_err.h:152
@ k_ra8_ok
Success – operation completed with all postconditions satisfied.
Definition ra8_err.h:119
@ k_ra8_err_null_ptr
Pointer was NULL where a valid pointer was required.
Definition ra8_err.h:478
@ k_ra8_err_protocol_error
Protocol-level error (e.g.
Definition ra8_err.h:429
@ k_ra8_err_timeout
Operation exceeded its time budget.
Definition ra8_err.h:188
@ k_ra8_err_invalid_size
Invalid size parameter (too large, too small, or misaligned).
Definition ra8_err.h:167
ra8_err_codes_t ra8_err_t
Canonical error-return type used by every ra8-firmware API.
Definition ra8_err.h:546
static bool internal_hex_nibble(uint8_t digit, uint8_t *out)
Decode one hexadecimal digit.
static ra8_err_t internal_receive_line(const ra8_net_provision_uart_t *uart, uint32_t timeout_ms, uint8_t *line, size_t line_capacity, size_t *line_length)
Receive exactly one newline-terminated provisioning record.
static ra8_err_t internal_decode_field(const uint8_t *line, size_t begin, size_t end, char *output, size_t capacity, size_t *out_length)
Decode one bounded hexadecimal field.
static ra8_err_t internal_validate_receiver(const ra8_net_provision_uart_t *uart, uint32_t timeout_ms)
Validate the injected receiver dependencies and timeout.
static ra8_err_t internal_decode_line(const uint8_t *line, size_t line_length, ra8_net_credentials_t *candidate)
Decode the three fields between already-validated framing bytes.
ra8_net_provision_text_byte_t
Control-byte boundaries rejected from decoded text fields.
@ k_ra8_net_provision_text_delete
ASCII DEL control byte.
@ k_ra8_net_provision_text_printable_min
First printable ASCII byte.
static bool internal_prefix_matches(const uint8_t *line)
Check the fixed version prefix without a libc comparison.
ra8_err_t ra8_net_provision_parse(const uint8_t *line, size_t line_length, ra8_net_credentials_t *out)
Parse one complete version-one ASCII-hex provisioning line.
static const uint8_t s_prefix[k_ra8_net_provision_prefix_bytes]
Exact protocol prefix, including the first field separator.
static bool internal_psk_is_hex(const char *psk)
Check whether a decoded 64-byte PSK contains only ASCII hex.
static ra8_err_t internal_validate_candidate(const ra8_net_credentials_t *candidate)
Validate decoded field lengths and string compatibility.
ra8_err_t ra8_net_provision_receive(const ra8_net_provision_uart_t *uart, uint32_t timeout_ms, ra8_net_credentials_t *out)
Prompt once and receive one provisioning line within a fixed timeout.
static bool internal_find_newline(const uint8_t *chunk, size_t length, size_t *newline_offset)
Locate a newline in one newly received chunk.
void ra8_net_provision_clear(ra8_net_credentials_t *credentials)
Explicitly erase one decoded credential record.
static bool internal_has_no_controls(const char *text, size_t length)
Check a decoded text field for C0 and DEL control bytes.
ra8_net_provision_hex_t
Numeric base of hexadecimal alphabet digits.
@ k_ra8_net_provision_hex_alpha_offset
Value represented by A or a.
Runtime network provisioning contract for EK-RA8D2 applications.
struct ra8_net_credentials ra8_net_credentials_t
struct ra8_net_provision_uart ra8_net_provision_uart_t
const char k_ra8_net_provision_ready_prompt[]
Exact non-secret line emitted before the receiver drains UART input.
@ k_ra8_net_provision_timeout_ms_max
Longest accepted receive timeout.
@ k_ra8_net_provision_psk_bytes_max
Maximum decoded PSK text bytes.
@ k_ra8_net_provision_ssid_bytes_max
Maximum decoded SSID bytes.
@ k_ra8_net_provision_line_bytes_max
Longest complete encoded line.
@ k_ra8_net_provision_prefix_bytes
Bytes in RA8NET1:.
@ k_ra8_net_provision_url_bytes_max
Maximum decoded optional URL.
Secure-comparison primitives for the crypto / secure-boot paths.
void ra8_secure_memzero(void *ptr, size_t len)
Securely zero a buffer such that the write cannot be optimised away.
Definition ra8_secure.c:37
uint8_t psk_len
Decoded PSK bytes, excluding the NUL.
char psk[k_ra8_net_provision_psk_bytes_max+1U]
NUL-terminated PSK storage.
uint16_t url_len
Decoded URL bytes, excluding the NUL.
char url[k_ra8_net_provision_url_bytes_max+1U]
NUL-terminated optional URL storage.
uint8_t ssid_len
Decoded SSID bytes, excluding the NUL.
char ssid[k_ra8_net_provision_ssid_bytes_max+1U]
NUL-terminated SSID storage.
ra8_net_provision_uart_read_fn read
Non-blocking input drain.
ra8_net_provision_wait_fn wait_ms
Incomplete-poll pacing operation.
ra8_net_provision_uart_write_fn write
Non-secret prompt transmitter.