#include "cryptoauthlib.h"
#include "basic/atca_helpers.h"
#include "crypto/atca_crypto_sw_sha2.h"
#include "jwt/atca_jwt.h"
#include <stdio.h>
static const char g_jwt_header[] = "{\"alg\":\"ES256\",\"typ\":\"JWT\"}";
void atca_jwt_check_payload_start(
atca_jwt_t* jwt
)
{
if (jwt && jwt->buf && jwt->cur && (jwt->cur < jwt->buflen - 1))
{
char c = jwt->buf[jwt->cur - 1];
if ('.' == c)
{
jwt->buf[jwt->cur++] = '{';
}
else if ('{' != c)
{
jwt->buf[jwt->cur++] = ',';
}
}
}
ATCA_STATUS atca_jwt_init(
atca_jwt_t* jwt,
char* buf,
uint16_t buflen
)
{
ATCA_STATUS ret = ATCA_BAD_PARAM;
size_t tSize;
if (jwt && buf && buflen)
{
jwt->buf = buf;
jwt->buflen = buflen;
jwt->cur = 0;
tSize = jwt->buflen;
ret = atcab_base64encode_((const uint8_t*)g_jwt_header, strlen(g_jwt_header), jwt->buf,
&tSize, atcab_b64rules_urlsafe);
if (ATCA_SUCCESS == ret)
{
jwt->cur += (uint16_t)tSize;
if (jwt->cur < jwt->buflen - 1)
{
jwt->buf[jwt->cur++] = '.';
}
else
{
ret = ATCA_INVALID_SIZE;
}
}
}
return ret;
}
ATCA_STATUS atca_jwt_finalize(
atca_jwt_t* jwt,
uint16_t key_id
)
{
ATCA_STATUS status;
uint16_t i;
size_t rem;
size_t tSize;
if (!jwt || !jwt->buf || !jwt->buflen || !jwt->cur)
{
return ATCA_BAD_PARAM;
}
if ('}' != jwt->buf[jwt->cur - 1])
{
jwt->buf[jwt->cur++] = '}';
}
for (i = 0; i < jwt->cur; i++)
{
if ('.' == jwt->buf[i])
{
i++;
break;
}
}
rem = (jwt->cur - i + ATCA_SIG_SIZE) * 4;
rem /= 3;
rem += 3;
if (rem > (size_t)(jwt->buflen - jwt->cur))
{
return ATCA_INVALID_SIZE;
}
rem = jwt->cur - i;
memmove(jwt->buf + jwt->buflen - jwt->cur, &jwt->buf[i], rem);
tSize = jwt->buflen;
status = atcab_base64encode_((uint8_t*)(jwt->buf + jwt->buflen - jwt->cur), rem,
&jwt->buf[i], &tSize, atcab_b64rules_urlsafe);
if (ATCA_SUCCESS != status)
{
return status;
}
jwt->cur = (uint16_t)(i + tSize);
if (jwt->cur >= jwt->buflen - 88)
{
return ATCA_INVALID_SIZE;
}
status = atcac_sw_sha2_256((const uint8_t*)jwt->buf, jwt->cur, (uint8_t*)(jwt->buf + jwt->buflen - 32));
if (ATCA_SUCCESS != status)
{
return status;
}
status = atcab_sign(key_id, (const uint8_t*)(jwt->buf + jwt->buflen - ATCA_SHA_DIGEST_SIZE),
(uint8_t*)(jwt->buf + jwt->buflen - 64));
if (ATCA_SUCCESS != status)
{
return status;
}
jwt->buf[jwt->cur++] = '.';
tSize = jwt->buflen - jwt->cur;
atcab_base64encode_((const uint8_t*)(jwt->buf + jwt->buflen - ATCA_SIG_SIZE), ATCA_SIG_SIZE,
&jwt->buf[jwt->cur], &tSize, atcab_b64rules_urlsafe);
jwt->cur += (uint16_t)tSize;
if (jwt->cur >= jwt->buflen)
{
return ATCA_INVALID_SIZE;
}
jwt->buf[jwt->cur] = 0;
return status;
}
ATCA_STATUS atca_jwt_add_claim_string(
atca_jwt_t* jwt,
const char* claim,
const char* value
)
{
int32_t written;
int32_t remaining;
if (jwt && jwt->buf && jwt->buflen && claim && value)
{
atca_jwt_check_payload_start(jwt);
remaining = jwt->buflen - jwt->cur;
written = snprintf(&jwt->buf[jwt->cur], remaining, "\"%s\":\"%s\"", claim, value);
if (0 < written && written < remaining)
{
jwt->cur += written;
return ATCA_SUCCESS;
}
else
{
return ATCA_GEN_FAIL;
}
}
else
{
return ATCA_BAD_PARAM;
}
}
ATCA_STATUS atca_jwt_add_claim_numeric(
atca_jwt_t* jwt,
const char* claim,
int32_t value
)
{
int32_t written;
int32_t remaining;
if (jwt && jwt->buf && jwt->buflen && claim)
{
atca_jwt_check_payload_start(jwt);
remaining = jwt->buflen - jwt->cur;
written = snprintf(&jwt->buf[jwt->cur], remaining, "\"%s\":%ld", claim, (long)value);
if (0 < written && written < remaining)
{
jwt->cur += written;
return 0;
}
else
{
return ATCA_GEN_FAIL;
}
}
else
{
return ATCA_BAD_PARAM;
}
}
ATCA_STATUS atca_jwt_verify(
const char* buf,
uint16_t buflen,
const uint8_t* pubkey
)
{
ATCA_STATUS status = ATCA_GEN_FAIL;
uint8_t digest[ATCA_KEY_SIZE];
uint8_t signature[ATCA_SIG_SIZE];
size_t sig_len = sizeof(signature);
const char* pStr = buf;
bool verified = false;
if (!buf || !buflen || !pubkey)
{
return ATCA_BAD_PARAM;
}
do
{
pStr = strchr(pStr, '.') + 1;
if (!pStr)
{
break;
}
pStr = strchr(pStr, '.') + 1;
if (!pStr)
{
break;
}
if (ATCA_SUCCESS != (status = atcab_base64decode_(pStr, strlen(pStr),
signature, &sig_len, atcab_b64rules_urlsafe)))
{
break;
}
if (ATCA_SUCCESS != (status = atcac_sw_sha2_256((const uint8_t*)buf, pStr - buf - 1, digest)))
{
break;
}
if (ATCA_SUCCESS != (status = atcab_verify_extern(digest, signature,
pubkey, &verified)))
{
break;
}
if (!verified)
{
status = ATCA_CHECKMAC_VERIFY_FAILED;
}
}
while (0);
return status;
}