Files
ESP32_Serial_Swiss_Army_Knife/tests/wolfssh_auth_contract/contract.c
T

247 lines
12 KiB
C

/* Control-flow doubles only: no cryptographic implementation or real credentials. */
#include <assert.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
typedef uint8_t byte;
typedef uint32_t word32;
#include "auth_types.h"
/* Reviewed production feature profile. Never enable certificates/none silently. */
#define WOLFSSH_NO_RSA
#if defined(WOLFSSH_CERTS) || defined(WOLFSSH_ALLOW_USERAUTH_NONE) || \
defined(WOLFSSH_NO_ECDSA) || defined(WOLFSSH_NO_ED25519) || \
defined(NO_FAILURE_ON_REJECTED)
#error "Unexpected wolfSSH auth-contract feature profile"
#endif
#define WLOG(...) ((void)0)
#define WMEMSET memset
#define WMEMCPY memcpy
#define WSTRNCAT strncat
#define XSTRLEN strlen
#define BOOLEAN_SZ 1
#define UINT32_SZ 4
#define LENGTH_SZ 4
#define MSG_ID_SZ 1
#define MAX_AUTH_STRING 80
#define WOLFSSH_MAX_PROMPTS 8
#define WC_MAX_DIGEST_SIZE 64
#define min(a,b) ((a) < (b) ? (a) : (b))
enum { WS_SUCCESS = 0, WS_ERROR = -1, WS_BUFFER_E = -2,
WS_BAD_ARGUMENT = -3, WS_USER_AUTH_E = -4, WS_AUTH_PENDING = -5,
WS_INVALID_ALGO_ID = -6, WS_CRYPTO_FAILED = -7, WS_BAD_USAGE = -8,
WS_WANT_WRITE = -9, WS_REKEYING = -10, WS_INVALID_CHANID = -11,
WS_WINDOW_FULL = -12 };
enum { ID_NONE, ID_UNKNOWN, ID_USERAUTH_PASSWORD, ID_USERAUTH_KEYBOARD,
ID_USERAUTH_PUBLICKEY, ID_ED25519, ID_ECDSA_SHA2_NISTP256,
ID_ECDSA_SHA2_NISTP384, ID_ECDSA_SHA2_NISTP521 };
enum { WOLFSSH_ENDPOINT_SERVER, CLIENT_USERAUTH_DONE = 20,
MSGID_USERAUTH_REQUEST, MSGID_USERAUTH_INFO_REQUEST, MSGID_CHANNEL_DATA,
WS_CHANNEL_ID_SELF };
enum wc_HashType { WC_HASH_TYPE_SHA };
typedef int wc_HashAlg;
typedef struct WOLFSSH WOLFSSH;
typedef struct {
int (*userAuthCb)(byte, WS_UserAuthData *, void *);
int (*userAuthResultCb)(byte, WS_UserAuthData *, void *);
int (*keyboardAuthCb)(WS_UserAuthData_Keyboard *, void *);
int (*userAuthTypesCb)(WOLFSSH *, void *);
} WOLFSSH_CTX;
struct WOLFSSH {
WOLFSSH_CTX *ctx;
void *userAuthCtx, *userAuthResultCtx, *keyboardAuthCtx;
int clientState, isKeying, error;
byte sessionId[32];
word32 sessionIdSz;
struct { byte *buffer; word32 length, plainSz; } outputBuffer;
struct { word32 promptCount; } kbAuth;
};
typedef struct {
word32 peerWindowSz, peerMaxPacketSz, maxPacketSz, peerChannel;
} WOLFSSH_CHANNEL;
static WOLFSSH_CHANNEL channel;
static const byte cannedKeyAlgoClient[] = { ID_ED25519, ID_ECDSA_SHA2_NISTP256 };
static const word32 cannedKeyAlgoClientSz = sizeof(cannedKeyAlgoClient);
static char events[64];
static unsigned event_count, groups;
static int auth_return, crypto_return, result_return, send_return;
static int expect_new_password;
static void event(char c) { assert(event_count + 1 < sizeof(events)); events[event_count++] = c; }
static void ato32(const byte *b, word32 *v) {
*v = (word32)b[0] << 24 | (word32)b[1] << 16 | (word32)b[2] << 8 | b[3];
}
static void c32toa(word32 v, byte *b) {
b[0] = v >> 24; b[1] = v >> 16; b[2] = v >> 8; b[3] = v;
}
static byte NameToId(const char *s, word32 n) {
static const struct { const char *s; byte id; } names[] = {
{"none", ID_NONE}, {"password", ID_USERAUTH_PASSWORD},
{"keyboard-interactive", ID_USERAUTH_KEYBOARD}, {"publickey", ID_USERAUTH_PUBLICKEY},
{"ssh-ed25519", ID_ED25519}, {"ecdsa-sha2-nistp256", ID_ECDSA_SHA2_NISTP256}
};
for (unsigned i = 0; i < sizeof(names)/sizeof(names[0]); ++i)
if (strlen(names[i].s) == n && !memcmp(s, names[i].s, n)) return names[i].id;
return ID_UNKNOWN;
}
static byte MatchIdLists(int side, const byte *id, word32 count, const byte *list, word32 n) {
for (word32 i = 0; i < n; ++i) if (*id == list[i]) return *id;
return ID_UNKNOWN;
}
static int wolfSSH_SetUsernameRaw(WOLFSSH *s, const byte *u, word32 n) { return WS_SUCCESS; }
static int SendUserAuthFailure(WOLFSSH *s, byte partial) { event('F'); return WS_SUCCESS; }
static int SendUserAuthPkOk(WOLFSSH *s, const byte *a, word32 an, const byte *k, word32 kn) {
event('P'); return WS_SUCCESS;
}
static int DoUserAuthRequestEd25519(WOLFSSH *s, WS_UserAuthData_PublicKey *p, WS_UserAuthData *a) {
event('C'); return crypto_return;
}
static int DoUserAuthRequestEcc(WOLFSSH *s, WS_UserAuthData_PublicKey *p,
enum wc_HashType h, byte *d, word32 n) {
event('C'); return crypto_return;
}
static enum wc_HashType HashForId(byte id) { return WC_HASH_TYPE_SHA; }
static int wc_HashGetDigestSize(enum wc_HashType h) { return 32; }
static int wc_HashInit(wc_HashAlg *h, enum wc_HashType id) { event('H'); return 0; }
static int HashUpdate(wc_HashAlg *h, enum wc_HashType id, const byte *b, word32 n) { return 0; }
static int wc_HashFinal(wc_HashAlg *h, enum wc_HashType id, byte *b) { return 0; }
static void wc_HashFree(wc_HashAlg *h, enum wc_HashType id) {}
static int PrepareUserAuthRequestKeyboard(WOLFSSH *s, word32 *n, WS_UserAuthData *a) {
event('Q'); return WS_SUCCESS;
}
static int BuildUserAuthRequestKeyboard(WOLFSSH *s, byte *b, word32 *n, WS_UserAuthData *a) {
event('B'); return WS_SUCCESS;
}
static int PreparePacket(WOLFSSH *s, word32 n) { event('T'); return WS_SUCCESS; }
static int BundlePacket(WOLFSSH *s) { event('B'); return WS_SUCCESS; }
static int wolfSSH_SendPacket(WOLFSSH *s) { event('S'); s->error = send_return; return send_return; }
static void PurgePacket(WOLFSSH *s) { event('X'); }
static WOLFSSH_CHANNEL *ChannelFind(WOLFSSH *s, word32 id, int kind) { return &channel; }
#include "actual.c"
static int authorize(byte method, WS_UserAuthData *a, void *ctx) {
event('A');
assert(method == a->type);
assert(a->usernameSz == 4 && !memcmp(a->username, "test", 4));
if (method == WOLFSSH_USERAUTH_PASSWORD) {
assert(a->sf.password.hasNewPassword == expect_new_password);
assert(a->sf.password.passwordSz == 5);
assert(!memcmp(a->sf.password.password, "dummy", 5));
}
return auth_return;
}
static int result(byte outcome, WS_UserAuthData *a, void *ctx) {
assert(a->type == WOLFSSH_USERAUTH_PUBLICKEY && a->sf.publicKey.hasSignature);
event(outcome == WOLFSSH_USERAUTH_SUCCESS ? 'R' : 'r');
return result_return;
}
/* Models the parent's registered rejecting prompt callback, not app accounting. */
static int reject_keyboard(WS_UserAuthData_Keyboard *k, void *ctx) {
event('K'); memset(k, 0, sizeof(*k)); return WS_ERROR;
}
static int allowed(WOLFSSH *s, void *ctx) {
return WOLFSSH_USERAUTH_PASSWORD | WOLFSSH_USERAUTH_PUBLICKEY;
}
static byte output[1024], packet[1024];
static word32 length;
static WOLFSSH_CTX context = { authorize, result, reject_keyboard, allowed };
static WOLFSSH ssh;
static void reset(void) {
memset(&ssh, 0, sizeof(ssh)); memset(output, 0, sizeof(output));
memset(events, 0, sizeof(events)); event_count = 0;
ssh.ctx = &context; ssh.outputBuffer.buffer = output; ssh.sessionIdSz = 32;
auth_return = WOLFSSH_USERAUTH_SUCCESS; crypto_return = WS_SUCCESS;
result_return = WS_SUCCESS; send_return = WS_SUCCESS; expect_new_password = 0;
length = 0;
}
static void blob(const void *s, word32 n) {
assert(length + 4 + n <= sizeof(packet));
c32toa(n, packet + length); length += 4;
memcpy(packet + length, s, n); length += n;
}
static void string(const char *s) { blob(s, (word32)strlen(s)); }
static void request(const char *method) { string("test"); string("ssh-connection"); string(method); }
static void key_request(int signed_key, const char *algorithm) {
request("publickey"); packet[length++] = signed_key; string(algorithm);
byte nested[128]; word32 n = (word32)strlen(algorithm);
c32toa(n, nested); memcpy(nested + 4, algorithm, n); nested[4 + n] = 42;
blob(nested, n + 5);
if (signed_key) {
c32toa(1, nested + 4 + n); nested[8 + n] = 42;
blob(nested, n + 9);
}
}
static int dispatch(void) { word32 idx = 0; return DoUserAuthRequest(&ssh, packet, length, &idx); }
static void check(const char *trace, int done) {
assert(!strcmp(events, trace));
assert((ssh.clientState == CLIENT_USERAUTH_DONE) == done); ++groups;
}
int main(void) {
const int rejected[] = { WOLFSSH_USERAUTH_INVALID_PUBLICKEY,
WOLFSSH_USERAUTH_FAILURE, WOLFSSH_USERAUTH_REJECTED,
WOLFSSH_USERAUTH_INVALID_USER, WOLFSSH_USERAUTH_INVALID_AUTHTYPE };
const char *algorithms[] = { "ssh-ed25519", "ecdsa-sha2-nistp256" };
for (unsigned a = 0; a < 2; ++a) {
for (unsigned r = 0; r < sizeof(rejected)/sizeof(rejected[0]); ++r) {
reset(); key_request(1, algorithms[a]); auth_return = rejected[r];
assert(dispatch() == WS_SUCCESS); check("AF", 0);
}
reset(); key_request(0, algorithms[a]); assert(dispatch() == WS_SUCCESS); check("AP", 0);
reset(); key_request(0, algorithms[a]); auth_return = WOLFSSH_USERAUTH_INVALID_PUBLICKEY;
assert(dispatch() == WS_SUCCESS); check("AF", 0);
reset(); key_request(1, algorithms[a]); crypto_return = WS_CRYPTO_FAILED;
result_return = WS_ERROR; /* Failure-result return is ignored. */
assert(dispatch() == WS_SUCCESS); check(a ? "AHCrF" : "ACrF", 0);
reset(); key_request(1, algorithms[a]); assert(dispatch() == WS_SUCCESS);
check(a ? "AHCR" : "ACR", 1);
reset(); key_request(1, algorithms[a]); result_return = WS_ERROR;
assert(dispatch() == WS_SUCCESS); check(a ? "AHCRF" : "ACRF", 0);
reset(); key_request(1, algorithms[a]); auth_return = WOLFSSH_USERAUTH_WOULD_BLOCK;
assert(dispatch() == WS_AUTH_PENDING); check("A", 0);
}
for (int fail = 0; fail < 2; ++fail) {
reset(); request("password"); packet[length++] = 0; string("dummy");
auth_return = fail ? WOLFSSH_USERAUTH_INVALID_PASSWORD : WOLFSSH_USERAUTH_SUCCESS;
assert(dispatch() == WS_SUCCESS); check(fail ? "AF" : "A", !fail);
}
reset(); request("password"); packet[length++] = 1; string("dummy"); string("new-dummy");
expect_new_password = 1; auth_return = WOLFSSH_USERAUTH_INVALID_AUTHTYPE;
assert(dispatch() == WS_SUCCESS); check("AF", 0);
/* Actual parser still calls auth when the new-password length is truncated. */
reset(); request("password"); packet[length++] = 1; string("dummy");
expect_new_password = 1; auth_return = WOLFSSH_USERAUTH_INVALID_AUTHTYPE;
assert(dispatch() == WS_SUCCESS); check("AF", 0);
const char *unsupported[] = { "none", "unrecognized" };
for (unsigned i = 0; i < 2; ++i) {
reset(); request(unsupported[i]); assert(dispatch() == WS_SUCCESS); check("F", 0);
}
reset(); key_request(1, "unsupported-key"); assert(dispatch() == WS_SUCCESS); check("F", 0);
reset(); key_request(1, "ssh-ed25519"); --length;
assert(dispatch() == WS_BUFFER_E); check("", 0);
reset(); request("keyboard-interactive"); string(""); string("");
assert(context.keyboardAuthCb != NULL); assert(dispatch() == WS_ERROR); check("KX", 0);
/* The library writes the message byte even on rejection, but does not send. */
assert(ssh.outputBuffer.length == 0 && output[0] == MSGID_USERAUTH_INFO_REQUEST);
char methods[MAX_AUTH_STRING]; int n = GetAllowedAuth(&ssh, methods);
methods[n] = '\0'; assert(!strcmp(methods, "publickey,password")); ++groups;
/* Execute actual copy/positive-consumed path, including deferred network send. */
for (int deferred = 0; deferred < 2; ++deferred) {
reset(); byte data[] = { 11, 22, 33, 44, 55 };
channel = (WOLFSSH_CHANNEL){ 100, 3, 10, 7 };
send_return = deferred ? WS_WANT_WRITE : WS_SUCCESS;
assert(SendChannelData(&ssh, 1, data, sizeof(data)) == 3);
assert(!memcmp(output + 9, data, 3)); memset(data, 0, 3);
assert(output[9] == 11 && output[10] == 22 && output[11] == 33);
assert(data[3] == 44 && channel.peerWindowSz == 97);
assert(ssh.outputBuffer.plainSz == (deferred ? 3U : 0U)); check("TBS", 0);
}
reset(); ssh.outputBuffer.plainSz = 2; send_return = WS_WANT_WRITE;
byte data[] = { 1, 2 }; assert(SendChannelData(&ssh, 1, data, 2) == WS_WANT_WRITE);
assert(output[9] == 0); check("S", 0);
printf("PASS: %u actual wolfSSH parser/control-flow cases\n", groups);
return 0;
}