Files

157 lines
6.2 KiB
C
Raw Permalink Normal View History

#include "vl_protocol.h"
#include "mbedtls/base64.h"
#include <string.h>
static int hex_digit(char c) {
if (c >= '0' && c <= '9') return c - '0';
if (c >= 'a' && c <= 'f') return c - 'a' + 10;
if (c >= 'A' && c <= 'F') return c - 'A' + 10;
return -1;
}
static void to_hex(const uint8_t *in, size_t n, char *out) {
const char *hex = "0123456789abcdef";
for (size_t i = 0; i < n; i++) {
out[i * 2] = hex[in[i] >> 4];
out[i * 2 + 1] = hex[in[i] & 15];
}
out[n * 2] = '\0';
}
int vl_key_from_hex(const char *hex, uint8_t key[32]) {
if (!hex || !key || strlen(hex) != 64) return -1;
for (size_t i = 0; i < 64; i++) if (hex_digit(hex[i]) < 0) return -1;
for (size_t i = 0; i < 32; i++)
key[i] = (uint8_t)((hex_digit(hex[i * 2]) << 4) | hex_digit(hex[i * 2 + 1]));
return 0;
}
int vl_nonce(vl_rng_fn rng, void *context, char out[23]) {
uint8_t random[16], base64[25];
size_t n = 0;
if (!rng || !out) return -1;
out[0] = '\0';
if (rng(context, random, sizeof(random)) != 0) return -1;
if (mbedtls_base64_encode(base64, sizeof(base64), &n, random, sizeof(random))) return -1;
if (n != 24) return -1;
for (size_t i = 0; i < 22; i++)
out[i] = base64[i] == '+' ? '-' : base64[i] == '/' ? '_' : (char)base64[i];
out[22] = '\0';
return 0;
}
int vl_uuid4(vl_rng_fn rng, void *context, char out[37]) {
uint8_t random[16];
char hex[33];
size_t p = 0;
if (!rng || !out) return -1;
out[0] = '\0';
if (rng(context, random, sizeof(random)) != 0) return -1;
random[6] = (uint8_t)((random[6] & 15) | 0x40);
random[8] = (uint8_t)((random[8] & 63) | 0x80);
to_hex(random, 16, hex);
for (size_t i = 0; i < 32; i++) {
if (i == 8 || i == 12 || i == 16 || i == 20) out[p++] = '-';
out[p++] = hex[i];
}
out[p] = '\0';
return 0;
}
int vl_sha256_begin(mbedtls_md_context_t *context) {
if (!context) return -1;
mbedtls_md_init(context);
const mbedtls_md_info_t *info = mbedtls_md_info_from_type(MBEDTLS_MD_SHA256);
if (!info) return -1;
int rc = mbedtls_md_setup(context, info, 0);
if (rc == 0) rc = mbedtls_md_starts(context);
return rc;
}
int vl_sha256_update(mbedtls_md_context_t *context, const void *bytes, size_t n) {
if (!context || (!bytes && n)) return -1;
return n ? mbedtls_md_update(context, bytes, n) : 0;
}
int vl_sha256_finish(mbedtls_md_context_t *context, char out[65]) {
uint8_t digest[32];
if (!context || !out) return -1;
out[0] = '\0';
int rc = mbedtls_md_finish(context, digest);
if (rc == 0) to_hex(digest, 32, out);
return rc;
}
int vl_sha256_hex(const void *bytes, size_t length, char out[65]) {
if (!out || (!bytes && length)) return -1;
out[0] = '\0';
mbedtls_md_context_t context;
int rc = vl_sha256_begin(&context);
if (rc == 0) rc = vl_sha256_update(&context, bytes, length);
if (rc == 0) rc = vl_sha256_finish(&context, out);
mbedtls_md_free(&context);
return rc;
}
/* Hash the exact LF-joined fields without building a large intermediate string. */
static int hmac_fields(const uint8_t key[32], const char **fields,
size_t count, char out[65]) {
if (!key || !fields || !out) return -1;
out[0] = '\0';
for (size_t i = 0; i < count; i++)
if (!fields[i] || strchr(fields[i], '\n') || strchr(fields[i], '\r')) return -1;
const mbedtls_md_info_t *info = mbedtls_md_info_from_type(MBEDTLS_MD_SHA256);
if (!info) return -1;
mbedtls_md_context_t context;
uint8_t digest[32];
mbedtls_md_init(&context);
int rc = mbedtls_md_setup(&context, info, 1);
if (rc == 0) rc = mbedtls_md_hmac_starts(&context, key, 32);
for (size_t i = 0; rc == 0 && i < count; i++) {
if (i) rc = mbedtls_md_hmac_update(&context, (const uint8_t *)"\n", 1);
if (rc == 0) rc = mbedtls_md_hmac_update(&context, (const uint8_t *)fields[i], strlen(fields[i]));
}
if (rc == 0) rc = mbedtls_md_hmac_finish(&context, digest);
mbedtls_md_free(&context);
if (rc == 0) to_hex(digest, 32, out);
return rc;
}
int vl_http_sign(const uint8_t key[32], const char *method, const char *path,
const char *device_id, const char *key_version,
const char *timestamp_ms, const char *nonce,
const char *request_id, const void *body, size_t body_length,
char signature_hex[65]) {
if (!signature_hex) return -1;
signature_hex[0] = '\0';
if (!method || strcmp(method, "POST") || !path || path[0] != '/') return -1;
char body_sha[65];
if (vl_sha256_hex(body, body_length, body_sha)) return -1;
const char *fields[] = {"DEVREQ1", method, path, device_id, key_version,
timestamp_ms, nonce, request_id, body_sha};
return hmac_fields(key, fields, 9, signature_hex);
}
int vl_ble_pack(const void *json_utf8, size_t length,
uint8_t *frame, size_t capacity, size_t *frame_length) {
if (!frame_length) return -1;
*frame_length = 0;
if (!json_utf8 || !frame || length == 0 || length > 2048 || capacity < length + 2) return -1;
frame[0] = (uint8_t)(length & 255);
frame[1] = (uint8_t)(length >> 8);
memcpy(frame + 2, json_utf8, length);
*frame_length = length + 2;
return 0;
}
int vl_ota_verify(vl_read_fn read, void *context, uint64_t expected_size,
const char *expected_sha256) {
uint8_t buffer[1024], expected[32], actual[32];
char hash[65];
uint64_t total = 0;
if (!read || !expected_size || vl_key_from_hex(expected_sha256, expected)) return -1;
mbedtls_md_context_t sha;
int rc = vl_sha256_begin(&sha);
while (rc == 0) {
size_t n = 0;
if (read(context, buffer, sizeof(buffer), &n) != 0 || n > sizeof(buffer)) { rc = -1; break; }
if (n == 0) break;
if ((uint64_t)n > expected_size - total) { rc = -1; break; }
total += n;
rc = vl_sha256_update(&sha, buffer, n);
}
if (rc == 0 && total != expected_size) rc = -1;
if (rc == 0) rc = vl_sha256_finish(&sha, hash);
mbedtls_md_free(&sha);
if (rc != 0 || vl_key_from_hex(hash, actual)) return -1;
return memcmp(actual, expected, 32) == 0 ? 0 : -1;
}