46static bool read_string(
const uint8_t *p,
size_t len,
size_t *off,
char *out,
size_t outcap)
50 uint32_t n = ((uint32_t)p[*off] << 24) | ((uint32_t)p[*off + 1] << 16) | ((uint32_t)p[*off + 2] << 8) |
51 (uint32_t)p[*off + 3];
57 memcpy(out, p + *off, n);
63static void put_u32(uint8_t *p, uint32_t v)
65 p[0] = (uint8_t)(v >> 24);
66 p[1] = (uint8_t)(v >> 16);
67 p[2] = (uint8_t)(v >> 8);
72static bool read_string_ref(
const uint8_t *p,
size_t len,
size_t *off,
const uint8_t **out, uint32_t *slen)
76 uint32_t n = ((uint32_t)p[*off] << 24) | ((uint32_t)p[*off + 1] << 16) | ((uint32_t)p[*off + 2] << 8) |
77 (uint32_t)p[*off + 3];
88static bool mpint_to_fixed(
const uint8_t *m, uint32_t mlen, uint8_t *out,
size_t outlen)
91 while (off < mlen && m[off] == 0)
93 uint32_t vlen = mlen - off;
96 memset(out, 0, outlen);
97 memcpy(out + (outlen - vlen), m + off, vlen);
102static bool parse_ssh_rsa_blob(
const uint8_t *blob, uint32_t blen, uint8_t n_be[
SSH_RSA_KEY_BYTES], uint8_t e_be[4])
107 if (!read_string_ref(blob, blen, &off, &type, &type_len))
109 if (type_len != 7 || memcmp(type,
"ssh-rsa", 7) != 0)
114 if (!read_string_ref(blob, blen, &off, &e_mp, &e_len))
116 if (!mpint_to_fixed(e_mp, e_len, e_be, 4))
121 if (!read_string_ref(blob, blen, &off, &n_mp, &n_len))
130static bool parse_ssh_ed25519_blob(
const uint8_t *blob, uint32_t blen, uint8_t pub[32])
135 if (!read_string_ref(blob, blen, &off, &type, &type_len))
137 if (type_len != 11 || memcmp(type,
"ssh-ed25519", 11) != 0)
141 if (!read_string_ref(blob, blen, &off, &pk, &pk_len))
151static bool parse_ssh_ecdsa_blob(
const uint8_t *blob, uint32_t blen, uint8_t pub[SSH_ECDSA_P256_PUB_LEN])
156 if (!read_string_ref(blob, blen, &off, &type, &type_len))
158 if (type_len != 19 || memcmp(type,
"ecdsa-sha2-nistp256", 19) != 0)
160 const uint8_t *curve;
162 if (!read_string_ref(blob, blen, &off, &curve, &curve_len))
164 if (curve_len != 8 || memcmp(curve,
"nistp256", 8) != 0)
168 if (!read_string_ref(blob, blen, &off, &q, &q_len))
170 if (q_len != SSH_ECDSA_P256_PUB_LEN || q[0] != 0x04)
172 memcpy(pub, q, SSH_ECDSA_P256_PUB_LEN);
177static bool parse_ecdsa_sig(
const uint8_t *sig, uint32_t slen, uint8_t out[SSH_ECDSA_P256_SIG_LEN])
184 if (!read_string_ref(sig, slen, &off, &r, &r_len) || !read_string_ref(sig, slen, &off, &s, &s_len))
186 return mpint_to_fixed(r, r_len, out, SSH_ECDSA_P256_COORD_LEN) &&
187 mpint_to_fixed(s, s_len, out + SSH_ECDSA_P256_COORD_LEN, SSH_ECDSA_P256_COORD_LEN);
201 if (!read_string(payload, len, &off, svc,
sizeof(svc)))
203 if (strcmp(svc,
"ssh-userauth") != 0)
207 static const char name[] =
"ssh-userauth";
208 uint32_t nl = (uint32_t)(
sizeof(name) - 1);
209 if (cap < 1 + 4 + nl)
212 put_u32(out + 1, nl);
213 memcpy(out + 5, name, nl);
224 memset(req, 0,
sizeof(*req));
229 if (!read_string(payload, len, &off, req->
user,
sizeof(req->
user)))
231 if (!read_string(payload, len, &off, req->
service,
sizeof(req->
service)))
233 if (!read_string(payload, len, &off, req->
method,
sizeof(req->
method)))
236 if (strcmp(req->
method,
"password") == 0)
246 else if (strcmp(req->
method,
"publickey") == 0)
252 if (!read_string(payload, len, &off, req->
pk_algo,
sizeof(req->
pk_algo)))
263 const uint8_t *sigblob;
264 uint32_t sigblob_len;
265 if (!read_string_ref(payload, len, &off, &sigblob, &sigblob_len))
269 const uint8_t *salgo;
271 if (!read_string_ref(sigblob, sigblob_len, &so, &salgo, &salgo_len))
288#if DETWS_SSH_ALLOW_PASSWORD
289 static const char methods[] =
"publickey,password";
291 static const char methods[] =
"publickey";
293 uint32_t ml = (uint32_t)(
sizeof(methods) - 1);
294 if (cap < 1 + 4 + ml + 1)
297 put_u32(out + 1, ml);
298 memcpy(out + 5, methods, ml);
299 out[5 + ml] = partial ? 1 : 0;
300 *out_len = 5 + ml + 1;
315static int build_pk_ok(
const SshAuthReq *req, uint8_t *out,
size_t *out_len,
size_t cap)
317 uint32_t al = (uint32_t)strnlen(req->
pk_algo,
sizeof(req->
pk_algo));
318 if (cap < (
size_t)1 + 4 + al + 4 + req->
pk_blob_len)
322 put_u32(out + o, al);
324 memcpy(out + o, req->
pk_algo, al);
340static int ssh_auth_handle_pubkey(uint8_t i,
const SshAuthReq *req, uint8_t *out,
size_t *out_len,
size_t cap)
349 "ecdsa-sha2-nistp256",
354 uint8_t ec_pub[SSH_ECDSA_P256_PUB_LEN];
367 return build_pk_ok(req, out, out_len, cap);
391 uint8_t ec_sig[SSH_ECDSA_P256_SIG_LEN];
419 return ssh_auth_handle_pubkey(i, &req, out, out_len, cap);
423#if DETWS_SSH_ALLOW_PASSWORD
431 for (
size_t k = 0; k <
sizeof(req.
password); k++)
#define MAX_SSH_CONNS
Maximum simultaneous SSH connections.
#define SSH_PKT_BUF_SIZE
Packet assembly buffer per SSH connection (bytes).
void ssh_auth_set_pubkey_cb(SshPubkeyCb cb)
Install the publickey-authorization callback (nullptr → all fail).
int ssh_auth_build_success(uint8_t *out, size_t *out_len, size_t cap)
Build SSH_MSG_USERAUTH_SUCCESS.
void ssh_auth_set_password_cb(SshPasswordCb cb)
Install the password-verification callback (nullptr → all fail).
int ssh_auth_handle_request(uint8_t i, const uint8_t *payload, size_t len, uint8_t *out, size_t *out_len, size_t cap)
Handle a USERAUTH_REQUEST end-to-end for slot i.
int ssh_auth_build_failure(uint8_t *out, size_t *out_len, size_t cap, bool partial)
Build SSH_MSG_USERAUTH_FAILURE advertising "password".
int ssh_auth_parse_request(const uint8_t *payload, size_t len, SshAuthReq *req)
Parse an SSH_MSG_USERAUTH_REQUEST into req.
int ssh_auth_handle_service_request(const uint8_t *payload, size_t len, uint8_t *out, size_t *out_len, size_t cap)
Handle SSH_MSG_SERVICE_REQUEST; emit SERVICE_ACCEPT for ssh-userauth.
SSH user-authentication layer (RFC 4252).
bool(* SshPasswordCb)(const char *user, const char *password)
Application callback that validates a username/password pair.
bool(* SshPubkeyCb)(const char *user, const uint8_t *blob, size_t blob_len)
Application callback that decides whether a public key is authorized for user. blob is the "ssh-rsa" ...
bool ssh_ecdsa_p256_verify(const uint8_t pub[SSH_ECDSA_P256_PUB_LEN], const uint8_t *msg, size_t mlen, const uint8_t sig[SSH_ECDSA_P256_SIG_LEN])
Verify a P-256 ECDSA signature (SHA-256) against an uncompressed public point.
NIST P-256 primitives for SSH: ECDSA signatures and ECDH (RFC 5656 / FIPS 186-4).
bool ssh_ed25519_verify(const uint8_t pub[32], const uint8_t *msg, size_t mlen, const uint8_t sig[64])
Ed25519 signatures (RFC 8032) for ssh-ed25519 host keys + client auth.
SSH binary packet protocol: framing, AES-256-CTR encryption, HMAC-SHA2-256 integrity,...
#define SSH_MSG_USERAUTH_REQUEST
#define SSH_MSG_USERAUTH_PK_OK
#define SSH_MSG_SERVICE_ACCEPT
#define SSH_MSG_SERVICE_REQUEST
#define SSH_MSG_USERAUTH_SUCCESS
#define SSH_MSG_USERAUTH_FAILURE
int ssh_rsa_verify(const uint8_t n_be[SSH_RSA_KEY_BYTES], const uint8_t e_be4[4], const uint8_t *msg, size_t msg_len, const uint8_t *sig, size_t sig_len, SshRsaHash hash)
Verify an RSA PKCS#1 v1.5 signature (rsa-sha2-256/512) with a public key.
RSA-SHA2-256/512 host-key signing and public-key serialization.
SshRsaHash
Hash algorithm selecting the RSA signature scheme (RFC 8332).
#define SSH_RSA_KEY_BYTES
RSA modulus and private exponent size in bytes (RSA-2048).
#define SSH_SHA256_DIGEST_LEN
SHA-256 digest length in bytes.
SshSession ssh_sess[MAX_SSH_CONNS]
Static pool of SSH session state (BSS), one per SSH slot.
SSH transport-layer protocol state machine (RFC 4253).
@ SSH_PHASE_OPEN
Authenticated; connection/channel protocol active.
Parsed SSH_MSG_USERAUTH_REQUEST.
const uint8_t * signature
Raw signature bytes (points into the payload).
bool is_pubkey
True if a publickey method-request was parsed.
char pk_algo[20]
Public-key algorithm name.
bool is_password
True if a password method-request was parsed.
char method[16]
Method name ("none", "password", "publickey").
char password[SSH_AUTH_PASS_MAX]
Password (method == "password").
char user[SSH_AUTH_USER_MAX]
User name, null-terminated.
uint32_t pk_blob_len
Length of pk_blob.
char service[32]
Requested service ("ssh-connection").
size_t signed_prefix_len
Length of signed_prefix (payload up to the signature).
const uint8_t * pk_blob
Public-key blob (points into the payload).
const uint8_t * signed_prefix
Bytes of the request that the signature covers.
uint32_t signature_len
Length of signature.
bool has_signature
True if the request carried a signature.
SshPhase phase
Current handshake phase.
bool authed
True after successful user authentication.