From 9ab9d96af56f6326db0e02f919f11eb3030793d0 Mon Sep 17 00:00:00 2001 From: Pol Henarejos Date: Tue, 21 Apr 2026 17:09:51 +0200 Subject: [PATCH] Add base64url routines. Signed-off-by: Pol Henarejos --- src/crypto_utils.c | 50 ++++++++++++++++++++++++++++++++++++++ src/crypto_utils.h | 2 ++ src/usb/lwip/rest.c | 20 +++++++++++++++ src/usb/lwip/rest.h | 9 ++++--- src/usb/lwip/rest_server.c | 5 ++-- 5 files changed, 80 insertions(+), 6 deletions(-) diff --git a/src/crypto_utils.c b/src/crypto_utils.c index 66658e0..8af31e8 100644 --- a/src/crypto_utils.c +++ b/src/crypto_utils.c @@ -22,6 +22,7 @@ #include "mbedtls/aes.h" #include "mbedtls/hkdf.h" #include "mbedtls/gcm.h" +#include "mbedtls/base64.h" #include "crypto_utils.h" #include "otp.h" #include "random.h" @@ -308,3 +309,52 @@ uint32_t crc32c(const uint8_t *buf, size_t len) { } return ~crc; } + +int base64url_encode(unsigned char *dst, size_t dlen, size_t *olen, const unsigned char *src, size_t slen) { + int rc = mbedtls_base64_encode(dst, dlen, olen, src, slen); + if (rc != 0) { + return rc; + } + for (size_t i = 0; i < *olen; i++) { + if (dst[i] == '+') { + dst[i] = '-'; + } + else if (dst[i] == '/') { + dst[i] = '_'; + } + } + uint8_t *p = dst + *olen - 1; + while (*p == '=') { + *p-- = '\0'; + (*olen)--; + } + return 0; +} + +int base64url_decode(unsigned char *dst, size_t dlen, size_t *olen, const unsigned char *src, size_t slen) { + // First convert from base64url to standard base64 + unsigned char *b64_src = (unsigned char *)malloc(slen + 2); // +2 for padding if needed + if (b64_src == NULL) { + return PICOKEYS_ERR_MEMORY_FATAL; + } + for (size_t i = 0; i < slen; i++) { + if (src[i] == '-') { + b64_src[i] = '+'; + } + else if (src[i] == '_') { + b64_src[i] = '/'; + } + else { + b64_src[i] = src[i]; + } + } + size_t padding = (4 - (slen % 4)) % 4; + for (size_t i = 0; i < padding; i++) { + b64_src[slen + i] = '='; + } + size_t b64_len = slen + padding; + + int rc = mbedtls_base64_decode(dst, dlen, olen, b64_src, b64_len); + free(b64_src); + return rc; +} diff --git a/src/crypto_utils.h b/src/crypto_utils.h index 1dbe13b..2f56317 100644 --- a/src/crypto_utils.h +++ b/src/crypto_utils.h @@ -66,6 +66,8 @@ extern int aes_encrypt_cfb_256(const uint8_t *key, const uint8_t *iv, uint8_t *d extern int aes_decrypt_cfb_256(const uint8_t *key, const uint8_t *iv, uint8_t *data, uint16_t len); extern mbedtls_ecp_group_id ec_get_curve_from_prime(const uint8_t *prime, size_t prime_len); extern uint32_t crc32c(const uint8_t *buf, size_t len); +extern int base64url_encode(unsigned char *dst, size_t dlen, size_t *olen, const unsigned char *src, size_t slen); +extern int base64url_decode(unsigned char *dst, size_t dlen, size_t *olen, const unsigned char *src, size_t slen); #define PIN_KDF_SIZE(x) (12 + (x) + 16) diff --git a/src/usb/lwip/rest.c b/src/usb/lwip/rest.c index e96b025..9c9a03e 100644 --- a/src/usb/lwip/rest.c +++ b/src/usb/lwip/rest.c @@ -20,6 +20,7 @@ #include "rest.h" #include #include "random.h" +#include "crypto_utils.h" #define REST_MAX_SESSIONS 4 @@ -34,6 +35,11 @@ rest_session_t *rest_session_create(const rest_session_role_t role, rest_session random_fill_buffer(rest_sessions[i].id, sizeof(rest_sessions[i].id)); rest_sessions[i].created_at = board_millis(); rest_sessions[i].last_activity_timestamp = rest_sessions[i].created_at; + size_t olen = 0; + if (base64url_encode(rest_sessions[i].id_str, sizeof(rest_sessions[i].id_str), &olen, (const unsigned char *)rest_sessions[i].id, sizeof(rest_sessions[i].id)) != 0) { + memset(&rest_sessions[i], 0, sizeof(rest_session_t)); + return NULL; + } return &rest_sessions[i]; } } @@ -54,6 +60,20 @@ rest_session_t *rest_session_get(const uint8_t *id, size_t id_len) { return NULL; } +rest_session_t *rest_session_get_by_id_str(const char *id_str) { + if (id_str == NULL || strlen(id_str) != 22) { + return NULL; + } + for (int i = 0; i < REST_MAX_SESSIONS; i++) { + if (rest_sessions[i].status != REST_SESSION_UNKNOWN && rest_sessions[i].status != REST_SESSION_EXPIRED && rest_sessions[i].status != REST_SESSION_TERMINATED) { + if (strcmp((const char *)rest_sessions[i].id_str, id_str) == 0) { + return &rest_sessions[i]; + } + } + } + return NULL; +} + int rest_session_terminate(const uint8_t *id, size_t id_len) { rest_session_t *session = rest_session_get(id, id_len); if (session == NULL) { diff --git a/src/usb/lwip/rest.h b/src/usb/lwip/rest.h index 87cc2d1..52e3322 100644 --- a/src/usb/lwip/rest.h +++ b/src/usb/lwip/rest.h @@ -25,6 +25,7 @@ #include #include #include "cJSON.h" +#include "mbedtls/base64.h" #define REST_MAX_REQUEST_SIZE 1024 #define REST_MAX_METHOD_SIZE 8 @@ -85,9 +86,9 @@ typedef struct { } rest_route_t; typedef enum { - REST_SESSION_NONE = 0, - REST_SESSION_USER = 0x1, - REST_SESSION_ADMIN = 0x2 + REST_SESSION_ROLE_NONE = 0, + REST_SESSION_ROLE_USER = 0x1, + REST_SESSION_ROLE_ADMIN = 0x2 } rest_session_role_t; typedef enum { @@ -102,6 +103,7 @@ typedef enum { typedef struct { uint8_t id[16]; + uint8_t id_str[25]; time_t last_activity_timestamp; time_t created_at; uint32_t last_seq; @@ -120,6 +122,7 @@ const rest_route_t *rest_get_routes(size_t *count); extern rest_session_t *rest_session_create(const rest_session_role_t role, rest_session_status_t status); extern rest_session_t *rest_session_get(const uint8_t *id, size_t id_len); +extern rest_session_t *rest_session_get_by_id_str(const char *id_str); extern int rest_session_terminate(const uint8_t *id, size_t id_len); extern int rest_session_update_activity(const uint8_t *id, size_t id_len); extern int rest_session_set_status(const uint8_t *id, size_t id_len, rest_session_status_t status); diff --git a/src/usb/lwip/rest_server.c b/src/usb/lwip/rest_server.c index c8a4d55..a4c314d 100644 --- a/src/usb/lwip/rest_server.c +++ b/src/usb/lwip/rest_server.c @@ -24,7 +24,6 @@ #include #include -#include "mbedtls/base64.h" #include "mbedtls/md.h" #include "mbedtls/hkdf.h" #include "crypto_utils.h" @@ -615,7 +614,7 @@ static int rest_verify_request_signature(const rest_request_t *request, const re if (md_info == NULL) { return PICOKEYS_ERR_MEMORY_FATAL; } - if (mbedtls_base64_decode(hmac_x, sizeof(hmac_x), &olen, (const unsigned char *)request->headers[REST_HEADER_X_SIGNATURE], strlen(request->headers[REST_HEADER_X_SIGNATURE])) != 0) { + if (base64url_decode(hmac_x, sizeof(hmac_x), &olen, (const unsigned char *)request->headers[REST_HEADER_X_SIGNATURE], strlen(request->headers[REST_HEADER_X_SIGNATURE])) != 0) { return PICOKEYS_EXEC_ERROR; } mbedtls_md_init(&ctx); @@ -707,7 +706,7 @@ void rest_handle_request(rest_conn_t *conn) { send_json_error(conn, 401, "authentication_required"); return; } - rest_session_t *session = rest_session_get((const uint8_t *)request->headers[REST_HEADER_X_SESSION_ID], strlen(request->headers[REST_HEADER_X_SESSION_ID])); + rest_session_t *session = rest_session_get_by_id_str(request->headers[REST_HEADER_X_SESSION_ID]); if (!session) { send_json_error(conn, 401, "authentication_required"); return;