Files
ESP32_Serial_Swiss_Army_Knife/tests/wolfssh_parser_contract/contract.c
T
Commander1024 51f835c22f Harden SSH parsing and add notice tooling
- Enforce exact service and channel names with bounded failure parsing
- Add hash-pinned offline notice assembly and regression coverage
- Record advisory dispositions, provenance, integration evidence, and
  remaining gates
2026-09-16 15:06:38 +02:00

262 lines
10 KiB
C

/* SPDX-License-Identifier: GPL-3.0-only
* Extracted wolfSSH functions retain upstream GPL notices in generated source.
* Crypto doubles test parser gating, not cryptographic validity. */
#include <assert.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <unistd.h>
typedef uint8_t byte;
typedef uint32_t word32;
#define WS_SUCCESS 0
#define WS_BUFFER_E -1
#define WS_BAD_ARGUMENT -2
#define WS_INVALID_CHANID -3
#define WS_OVERFLOW_E -4
#define WS_MEMORY_E -5
#define WS_INVALID_ALGO_ID -6
#define WS_CRYPTO_FAILED -7
#define WS_ECC_E -8
#define WS_ED25519_E -9
#define WS_INVALID_STATE_E -10
#define WS_CHANOPEN_FAILED -11
#define MSGID_USERAUTH_REQUEST 50
#define MSG_ID_SZ 1
#define BOOLEAN_SZ 1
#define Ed25519 0
#define UINT32_SZ 4
#define WOLFSSH_MAX_NAMESZ 32
#define CLIENT_USERAUTH_REQUEST_DONE 42
#define WS_CHANNEL_ID_SELF 0
#define WLOG(...) ((void)0)
#define WOLFSSH_UNUSED(x) ((void)(x))
#define WMEMCPY memcpy
#define WMEMCMP memcmp
#define WMALLOC(n,h,t) malloc(n)
#define WFREE(p,h,t) free(p)
#define ECDSA_ASN_SIG_SZ 80
#define INVALID_DEVID -1
#define WC_SIGNATURE_TYPE_ECC 1
struct context { void *heap; };
typedef struct {
struct context *ctx;
int clientState;
const byte *sessionId;
word32 sessionIdSz;
} WOLFSSH;
typedef struct { word32 peerWindowSz; } WOLFSSH_CHANNEL;
typedef struct {
const byte *publicKey, *publicKeyType, *signature;
word32 publicKeySz, publicKeyTypeSz, signatureSz;
const byte *dataToSign;
} WS_UserAuthData_PublicKey;
typedef struct {
word32 usernameSz, serviceNameSz, authNameSz;
} WS_UserAuthData;
typedef struct { int unused; } ed25519_key;
typedef struct { int unused; } ecc_key;
enum wc_HashType { HASH_SHA256 };
static unsigned imports, converts, verifies, finds, cases;
static unsigned ed_imports, ed_starts, ed_updates, ed_finals;
static const byte *expected_r, *expected_s, *expected_ed_sig;
static word32 expected_r_sz, expected_s_sz, expected_ed_sig_sz;
static byte ed_message[512];
static word32 ed_message_sz;
static int verify_failure;
static uintptr_t nested_begin, nested_end, nested_outer_end;
static WOLFSSH_CHANNEL channel;
static WOLFSSH_CHANNEL *ChannelFind(WOLFSSH *ssh, word32 id, int side)
{ finds++; return id == 7 ? &channel : NULL; }
static void ato32(const byte *p, word32 *v)
{
uintptr_t address=(uintptr_t)p;
if (nested_begin != 0 && address>=nested_begin && address<nested_outer_end)
assert(address<=nested_end && 4<=nested_end-address);
*v = ((word32)p[0]<<24) | ((word32)p[1]<<16) | ((word32)p[2]<<8) | p[3];
}
static void put(byte *p, word32 v)
{ p[0]=v>>24; p[1]=v>>16; p[2]=v>>8; p[3]=v; }
static int wc_ecc_init_ex(ecc_key *k, void *h, int id) { return 0; }
static int wc_ecc_import_x963(const byte *p, word32 n, ecc_key *k)
{ imports++; return 0; }
static void wc_ecc_free(ecc_key *k) {}
static int wc_ecc_rs_raw_to_sig(const byte *r, word32 rn, const byte *s,
word32 sn, byte *out, word32 *n)
{
converts++;
if (expected_r != NULL) {
assert(rn==expected_r_sz && sn==expected_s_sz);
assert(memcmp(r,expected_r,rn)==0 && memcmp(s,expected_s,sn)==0);
}
return 0;
}
static int wc_SignatureVerifyHash(enum wc_HashType h, int t, byte *d,
word32 dn, byte *s, word32 sn, ecc_key *k, size_t kn)
{ verifies++; return verify_failure; }
static void c32toa(word32 v, byte *p) { put(p,v); }
static int wc_ed25519_init_ex(ed25519_key *key, void *heap, int id) { return 0; }
static void wc_ed25519_free(ed25519_key *key) {}
static int wc_ed25519_import_public(const byte *p, word32 n, ed25519_key *key)
{ ed_imports++; assert(n==32); return 0; }
static int wc_ed25519_verify_msg_init(const byte *sig, word32 n,
ed25519_key *key, byte type, const byte *context, byte contextSz)
{
ed_starts++;
assert(n==expected_ed_sig_sz && memcmp(sig,expected_ed_sig,n)==0);
ed_message_sz=0;
return 0;
}
static int wc_ed25519_verify_msg_update(const byte *p, word32 n, ed25519_key *key)
{
ed_updates++;
assert(n<=sizeof(ed_message)-ed_message_sz);
if (n != 0)
memcpy(ed_message+ed_message_sz,p,n);
ed_message_sz+=n;
return 0;
}
static int wc_ed25519_verify_msg_final(const byte *sig, word32 n,
int *status, ed25519_key *key)
{
ed_finals++;
assert(n==expected_ed_sig_sz && memcmp(sig,expected_ed_sig,n)==0);
*status=!verify_failure;
return 0;
}
#include "actual.c"
static void parsers(byte *end)
{
struct context ctx = {0}; WOLFSSH ssh = {.ctx=&ctx, .clientState=9};
const word32 lengths[] = {0,1,2,3,4,27,28,31,32,33,255,UINT32_MAX-4,UINT32_MAX};
for (word32 n=0; n<=64; n++) {
byte *p=end-n;
for (unsigned j=0; j<sizeof(lengths)/sizeof(*lengths); j++) {
memset(p, 'x', n);
if (n>=4) put(p,lengths[j]);
word32 idx=0, value=123;
int good=n>=4 && lengths[j]<=n-4;
assert(GetSize(&value,p,n,&idx)==(good?0:WS_BUFFER_E));
idx=0;
assert(DoIgnore(&ssh,p,n,&idx)==(good?0:WS_BUFFER_E));
if (good) assert(idx==4+lengths[j]);
idx=0; ssh.clientState=9;
int bounded=good && lengths[j]<WOLFSSH_MAX_NAMESZ;
assert(DoServiceRequest(&ssh,p,n,&idx)==
(bounded?WS_INVALID_STATE_E:WS_BUFFER_E));
assert(ssh.clientState==9 && idx==0);
char out[10]; memset(out, 0x55, sizeof(out));
word32 cap=8; idx=0;
assert(GetString(out+1,&cap,p,n,&idx)==(good?0:WS_BUFFER_E));
assert(out[0]==0x55 && out[9]==0x55);
if(good) { assert(cap==(lengths[j]<8?lengths[j]:7)); assert(out[cap+1]==0); }
cap=0; idx=0;
assert(GetString((char*)end,&cap,p,n,&idx)==WS_BUFFER_E);
assert(idx==0);
cases++;
}
word32 invalids[]={n,n+1,UINT32_MAX-3,UINT32_MAX};
for(unsigned j=0;j<4;j++) {
word32 idx=invalids[j], v=0;
assert(GetSize(&v,p,n,&idx)==WS_BUFFER_E);
idx=invalids[j]; assert(DoIgnore(&ssh,p,n,&idx)==WS_BUFFER_E);
idx=invalids[j]; ssh.clientState=9;
assert(DoServiceRequest(&ssh,p,n,&idx)==WS_BUFFER_E);
assert(ssh.clientState==9 && idx==invalids[j]); cases++;
}
}
/* Nonzero packet offsets and exact-end empty strings. */
byte p[12]={0}; put(p+3,5); word32 idx=3;
assert(DoIgnore(&ssh,p,12,&idx)==0 && idx==12);
put(p+3,0); idx=3;
assert(DoServiceRequest(&ssh,p,7,&idx)==WS_INVALID_STATE_E && idx==3);
}
static void windows(byte *end)
{
WOLFSSH ssh={0};
for(word32 n=0;n<8;n++) {
byte *p=end-n; memset(p,0,n); word32 idx=0;
channel.peerWindowSz=123; finds=0;
assert(DoChannelWindowAdjust(&ssh,p,n,&idx)==WS_BUFFER_E);
assert(channel.peerWindowSz==123 && finds==0 && idx==0); cases++;
}
word32 values[]={0,1,2,0x7fffffff,0xfffffffe,UINT32_MAX};
for(unsigned a=0;a<6;a++) for(unsigned b=0;b<6;b++) {
byte p[11]={0}; put(p+3,7); put(p+7,values[b]); word32 idx=3;
channel.peerWindowSz=values[a];
int overflow=values[b]>UINT32_MAX-values[a];
assert(DoChannelWindowAdjust(&ssh,p,11,&idx)==(overflow?WS_OVERFLOW_E:0));
assert(channel.peerWindowSz==(overflow?values[a]:values[a]+values[b]));
assert(idx==11); cases++;
}
byte p[8]={0}; word32 idx=0; channel.peerWindowSz=12;
assert(DoChannelWindowAdjust(&ssh,p,8,&idx)==WS_INVALID_CHANID);
assert(channel.peerWindowSz==12);
}
static word32 string(byte *p, const byte *s, word32 n)
{ put(p,n); memcpy(p+4,s,n); return n+4; }
static void ecc(byte *end)
{
const byte type[]="ecdsa-sha2-nistp256";
const word32 size=sizeof(type)-1;
byte *expected=end-size; memcpy(expected,type,size);
struct context ctx={0}; WOLFSSH ssh={.ctx=&ctx}; byte digest[32]={0};
for(int which=0;which<2;which++) for(int mode=0;mode<6;mode++) {
byte key[128]={0},sig[128]={0}, bad[40]={0};
memcpy(bad,type,size); word32 n=size;
if(mode==1) bad[0]='X'; /* equal length mismatch */
if(mode==2) n--; /* matching prefix, shorter */
if(mode==3) n++; /* expected ends at guard page */
if(mode==4) n=0;
if(mode==5) n=sizeof(bad);
word32 k=string(key,which==0?bad:type,which==0?n:size);
k+=string(key+k,(const byte*)"nistp256",8);
k+=string(key+k,(const byte*)"Q",1);
word32 s=string(sig,which==1?bad:type,which==1?n:size);
put(sig+s,10); s+=4;
s+=string(sig+s,(const byte*)"r",1);
s+=string(sig+s,(const byte*)"s",1);
WS_UserAuthData_PublicKey pk={.publicKey=key, .publicKeyType=expected,
.signature=sig, .publicKeySz=k, .publicKeyTypeSz=size, .signatureSz=s};
imports=converts=verifies=0;
int ret=DoUserAuthRequestEcc(&ssh,&pk,HASH_SHA256,digest,sizeof(digest));
if(mode==0) assert(ret==0 && imports==1 && converts==1 && verifies==1);
else {
assert(ret==(which==0?WS_CRYPTO_FAILED:WS_INVALID_ALGO_ID));
assert(imports==(unsigned)which && converts==0 && verifies==0);
}
cases++;
if (mode==0 && which==0) {
for (word32 cut=0; cut<k; cut++) {
pk.publicKeySz=cut; verifies=0;
assert(DoUserAuthRequestEcc(&ssh,&pk,HASH_SHA256,digest,sizeof(digest))!=0);
assert(verifies==0); cases++;
}
pk.publicKeySz=k;
for (word32 cut=0; cut<s; cut++) {
pk.signatureSz=cut; verifies=0;
assert(DoUserAuthRequestEcc(&ssh,&pk,HASH_SHA256,digest,sizeof(digest))!=0);
assert(verifies==0); cases++;
}
}
}
}
#include "auth_framing.c"
#include "remaining.c"
int main(void)
{
long page=sysconf(_SC_PAGESIZE); assert(page>0);
byte *map=mmap(NULL,(size_t)page*2,PROT_READ|PROT_WRITE,MAP_PRIVATE|MAP_ANONYMOUS,-1,0);
assert(map!=MAP_FAILED && mprotect(map+page,page,PROT_NONE)==0);
parsers(map+page); windows(map+page); ecc(map+page);
remaining_parsers(map+page);
ecc_framing(map+page); ed25519_framing(map+page);
assert(munmap(map,(size_t)page*2)==0);
printf("PASS: %u parser/window/ECC/Ed25519 cases, guard pages + UBSan trap\n",cases);
return 0;
}