19static bool ct_eq(
const char *a,
const char *b,
size_t n)
22 for (
size_t i = 0; i < n; i++)
23 diff |= (uint8_t)(a[i] ^ b[i]);
32static bool jwt_split(
const char *token,
size_t token_len,
size_t *signing_len,
const char **sig,
size_t *sig_len)
34 const char *d1 = (
const char *)memchr(token,
'.', token_len);
37 size_t rem = token_len - (size_t)(d1 + 1 - token);
38 const char *d2 = (
const char *)memchr(d1 + 1,
'.', rem);
41 size_t rem2 = token_len - (size_t)(d2 + 1 - token);
42 if (memchr(d2 + 1,
'.', rem2))
44 *signing_len = (size_t)(d2 - token);
53static bool jwt_header_alg_is_hs256(
const char *header,
size_t hlen)
60 const char *p = strstr((
const char *)buf,
"\"alg\"");
64 while (*p ==
' ' || *p ==
':' || *p ==
'\t')
69 return strncmp(p,
"HS256", 5) == 0 && p[5] ==
'"';
72bool jwt_verify_hs256(
const char *token,
size_t token_len,
const uint8_t *secret,
size_t secret_len)
80 if (!jwt_split(token, token_len, &signing_len, &sig, &sig_len))
84 const char *d1 = (
const char *)memchr(token,
'.', token_len);
85 if (!jwt_header_alg_is_hs256(token, (
size_t)(d1 - token)))
93 ssh_hmac_sha256(secret, secret_len, (
const uint8_t *)token, signing_len, mac);
98 return ct_eq(computed, sig, 43);
101bool jwt_bearer_valid(
const char *auth_header,
const uint8_t *secret,
size_t secret_len)
103 if (!auth_header || strncasecmp(auth_header,
"Bearer ", 7) != 0)
105 const char *tok = auth_header + 7;
108 return jwt_verify_hs256(tok, strnlen(tok,
DETWS_JWT_MAX_LEN + 1), secret, secret_len);
111bool jwt_time_valid(
const char *token,
size_t token_len,
long now_epoch,
long leeway_s)
113 if (!token || now_epoch <= 0)
118 if (jwt_claim_int(token, token_len,
"exp", &exp) && now_epoch - exp > leeway_s)
122 if (jwt_claim_int(token, token_len,
"nbf", &nbf) && nbf - now_epoch > leeway_s)
128bool jwt_verify_hs256_at(
const char *token,
size_t token_len,
const uint8_t *secret,
size_t secret_len,
long now_epoch,
131 return jwt_verify_hs256(token, token_len, secret, secret_len) &&
132 jwt_time_valid(token, token_len, now_epoch, leeway_s);
135bool jwt_bearer_valid_at(
const char *auth_header,
const uint8_t *secret,
size_t secret_len,
long now_epoch,
138 if (!auth_header || strncasecmp(auth_header,
"Bearer ", 7) != 0)
140 const char *tok = auth_header + 7;
143 return jwt_verify_hs256_at(tok, strnlen(tok,
DETWS_JWT_MAX_LEN + 1), secret, secret_len, now_epoch, leeway_s);
146bool jwt_claim_int(
const char *token,
size_t token_len,
const char *name,
long *out)
148 if (!token || !name || !out)
151 const char *d1 = (
const char *)memchr(token,
'.', token_len);
154 size_t rem = token_len - (size_t)(d1 + 1 - token);
155 const char *d2 = (
const char *)memchr(d1 + 1,
'.', rem);
158 const char *payload = d1 + 1;
159 size_t payload_len = (size_t)(d2 - payload);
168 int kn = snprintf(key,
sizeof(key),
"\"%s\"", name);
169 if (kn <= 0 || kn >= (
int)
sizeof(key))
171 const char *p = strstr((
const char *)buf, key);
175 while (*p ==
' ' || *p ==
':' || *p ==
'\t')
183 if (*p <
'0' || *p >
'9')
186 while (*p >=
'0' && *p <=
'9')
187 v = v * 10UL + (
unsigned long)(*p++ -
'0');
188 *out = neg ? (long)(0UL - v) : (long)v;
192bool jwt_claim_str(
const char *token,
size_t token_len,
const char *name,
char *out,
size_t out_cap)
194 if (!token || !name || !out || out_cap == 0)
198 const char *d1 = (
const char *)memchr(token,
'.', token_len);
201 size_t rem = token_len - (size_t)(d1 + 1 - token);
202 const char *d2 = (
const char *)memchr(d1 + 1,
'.', rem);
205 const char *payload = d1 + 1;
206 size_t payload_len = (size_t)(d2 - payload);
215 int kn = snprintf(key,
sizeof(key),
"\"%s\"", name);
216 if (kn <= 0 || kn >= (
int)
sizeof(key))
218 const char *p = strstr((
const char *)buf, key);
222 while (*p ==
' ' || *p ==
':' || *p ==
'\t')
228 while (*p && *p !=
'"' && i + 1 < out_cap)
230 if (*p ==
'\\' && p[1])
243bool jwt_scope_allows(
const char *scope_claim,
const char *required)
245 if (!scope_claim || !required || !*required)
248 const char *p = scope_claim;
253 const char *start = p;
254 while (*p && *p !=
' ')
256 if ((
size_t)(p - start) == rlen && memcmp(start, required, rlen) == 0)
#define DETWS_JWT_MAX_LEN
Maximum accepted JWT length in bytes (header.payload.signature).
size_t base64url_decode(const char *src, size_t src_len, uint8_t *dst, size_t dst_cap)
Decode src_len characters of base64url (RFC 4648 section 5, '-'/'_' alphabet; an '=' ends the input).
size_t base64url_encode(const uint8_t *src, size_t src_len, char *dst)
Encode src_len bytes as base64url (RFC 4648 section 5): '-' / '_' in place of '+' / '/',...
Zero-heap JWT (JSON Web Token) bearer-auth verification, HS256.
void ssh_hmac_sha256(const uint8_t *key, size_t key_len, const uint8_t *data, size_t len, uint8_t mac[SSH_HMAC_SHA256_LEN])
Compute HMAC-SHA2-256 over a single contiguous buffer.
HMAC-SHA2-256 (RFC 2104 + FIPS 198-1).
#define SSH_HMAC_SHA256_LEN
HMAC-SHA2-256 output length in bytes.