Files
ESP32_Serial_Swiss_Army_Knife/tests/ssh_protocol_policy/context.c
T
Commander1024 cdc9c7335a Add Phase 9C security hardening
Generate exact-hash SDK source overrides without modifying dependencies.
Harden
SSH allocation and algorithm policy, tighten web authentication cleanup,
and add
focused host contract tests and documentation.
2026-09-15 22:12:57 +02:00

183 lines
6.3 KiB
C

/* Actual create_context() + actual policy; identity/vendor/callback doubles. */
#include <assert.h>
#include <stdio.h>
#include <string.h>
#include "ssh_protocol_policy.h"
#include "context_constants.h"
typedef int esp_err_t;
enum { ESP_OK = 0, ESP_FAIL = -1, ESP_ERR_NO_MEM = 0x101 };
static WOLFSSH_CTX candidate, *s_context;
static unsigned copies, news, imports, wipes, setters, callbacks, frees;
static unsigned fail_setter;
static int copy_error, allocation_fail, import_error, setter_error;
static unsigned char *identity;
static size_t identity_capacity;
static void unpublished(void)
{
assert(s_context == NULL);
}
static void wiped(void)
{
assert(wipes == 1);
assert(identity_capacity == SSH_SECURITY_PRIVATE_KEY_DER_CAPACITY);
/* Only called while the extracted create_context stack frame is alive. */
for (size_t i = 0; i < identity_capacity; ++i) assert(identity[i] == 0);
}
static esp_err_t ssh_security_copy_private_key(unsigned char *out, size_t capacity,
size_t *length)
{
unpublished();
assert(++copies == 1);
assert(capacity == SSH_SECURITY_PRIVATE_KEY_DER_CAPACITY);
for (size_t i = 0; i < capacity; ++i) assert(out[i] == 0);
identity = out;
identity_capacity = capacity;
/* Mark the unused tail too, so a short wipe cannot pass this test. */
memset(out, 0x6d, capacity);
*length = 31;
return copy_error;
}
static void secure_wipe(void *ptr, size_t length)
{
unpublished();
assert(ptr == identity && length == identity_capacity);
assert(wipes++ == 0);
memset(ptr, 0, length);
}
static WOLFSSH_CTX *wolfSSH_CTX_new(int endpoint, void *heap)
{
unpublished();
assert(copies == 1 && wipes == 0 && imports == 0);
assert(endpoint == WOLFSSH_ENDPOINT_SERVER && heap == NULL);
assert(++news == 1);
return allocation_fail ? NULL : &candidate;
}
static int wolfSSH_CTX_UsePrivateKey_buffer(WOLFSSH_CTX *ctx,
const unsigned char *key, word32 length, int format)
{
unpublished();
assert(ctx == &candidate && news == 1 && wipes == 0);
assert(++imports == 1);
assert(key == identity && length == 31 && format == WOLFSSH_FORMAT_ASN1);
for (size_t i = 0; i < identity_capacity; ++i) assert(key[i] == 0x6d);
return import_error;
}
static void wolfSSH_CTX_free(WOLFSSH_CTX *ctx)
{
unpublished();
wiped();
assert(ctx == &candidate && news == 1 && callbacks == 0);
assert(++frees == 1);
}
static int set_list(WOLFSSH_CTX *ctx, const char *list, const char **field,
unsigned step)
{
unpublished();
wiped();
assert(imports == 1 && import_error == 0 && frees == 0 && callbacks == 0);
assert(ctx == &candidate && ++setters == step);
assert(list != NULL && list[0] != '\0');
if (step == fail_setter) return setter_error;
*field = list;
return WS_SUCCESS;
}
#define SETTER(name, field, step) \
int wolfSSH_CTX_SetAlgoList##name(WOLFSSH_CTX *ctx, const char *list) \
{ return set_list(ctx, list, &ctx->field, step); }
SETTER(Kex, algoListKex, 1)
SETTER(Key, algoListKey, 2)
SETTER(Cipher, algoListCipher, 3)
SETTER(Mac, algoListMac, 4)
SETTER(KeyAccepted, algoListKeyAccepted, 5)
static void bounded_ssh_receive(void) {}
static void authenticate_user(void) {}
static void allowed_auth_types(void) {}
static void authentication_result(void) {}
static void reject_keyboard_auth(void) {}
static void accept_shell(void) {}
static void reject_channel_request(void) {}
static int callback(WOLFSSH_CTX *ctx, void (*actual)(void),
void (*expected)(void), unsigned step)
{
unpublished();
wiped();
assert(ctx == &candidate && setters == 5 && fail_setter == 0 && frees == 0);
assert(++callbacks == step && actual == expected);
return WS_SUCCESS;
}
#define CALLBACK(name, expected, step) \
static int name(WOLFSSH_CTX *ctx, void (*cb)(void)) \
{ return callback(ctx, cb, expected, step); }
CALLBACK(wolfSSH_SetIORecv, bounded_ssh_receive, 1)
CALLBACK(wolfSSH_SetUserAuth, authenticate_user, 2)
CALLBACK(wolfSSH_SetUserAuthTypes, allowed_auth_types, 3)
CALLBACK(wolfSSH_SetUserAuthResult, authentication_result, 4)
CALLBACK(wolfSSH_SetKeyboardAuthPrompts, reject_keyboard_auth, 5)
CALLBACK(wolfSSH_CTX_SetChannelReqShellCb, accept_shell, 6)
CALLBACK(wolfSSH_CTX_SetChannelReqExecCb, reject_channel_request, 7)
CALLBACK(wolfSSH_CTX_SetChannelReqSubsysCb, reject_channel_request, 8)
#include "context_actual.c"
static void reset(void)
{
memset(&candidate, 0, sizeof(candidate));
s_context = NULL;
copies = news = imports = wipes = setters = callbacks = frees = 0;
fail_setter = 0;
copy_error = allocation_fail = import_error = setter_error = 0;
identity = NULL;
identity_capacity = 0;
}
static void failed(int expected)
{
assert(create_context() == expected);
assert(s_context == NULL && callbacks == 0 && wipes == 1 && copies == 1);
/* identity points at a retired stack frame now: never inspect it here. */
identity = NULL;
}
int main(void)
{
unsigned cases = 0;
reset();
copy_error = 0x4321;
failed(copy_error);
assert(news == 0 && imports == 0 && setters == 0 && frees == 0);
++cases;
reset();
allocation_fail = 1;
failed(ESP_ERR_NO_MEM);
assert(news == 1 && imports == 0 && setters == 0 && frees == 0);
++cases;
for (unsigned sign = 0; sign < 2; ++sign) {
reset();
import_error = sign ? 7001 : -7001;
failed(ESP_FAIL);
assert(news == 1 && imports == 1 && setters == 0 && frees == 1);
++cases;
}
for (unsigned step = 1; step <= 5; ++step) {
for (unsigned sign = 0; sign < 2; ++sign) {
reset();
fail_setter = step;
setter_error = sign ? (int)(8000 + step) : -(int)(8000 + step);
failed(ESP_FAIL);
assert(news == 1 && imports == 1 && setters == step && frees == 1);
++cases;
}
}
reset();
assert(create_context() == ESP_OK);
identity = NULL;
assert(s_context == &candidate);
assert(copies == 1 && news == 1 && imports == 1 && wipes == 1);
assert(setters == 5 && callbacks == 8 && frees == 0);
++cases;
assert(cases == 15);
puts("PASS: actual create_context + policy: 15 cases, full identity wipe before policy/callbacks/free, exact cleanup, publication only after success");
return 0;
}