Skip to content

Commit 43426e7

Browse files
committed
Add l8w8jwt component
1 parent 9e9a935 commit 43426e7

File tree

17 files changed

+828
-1
lines changed

17 files changed

+828
-1
lines changed

.github/ISSUE_TEMPLATE/bug-report.yml

+1
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ body:
3636
- jsmn
3737
- json_generator
3838
- json_parser
39+
- l8w8jwt
3940
- led_strip
4041
- libsodium
4142
- nghttp

.github/workflows/upload_component.yml

+1
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ jobs:
5151
json_generator;
5252
json_parser;
5353
led_strip;
54+
l8w8jwt;
5455
libsodium;
5556
nghttp;
5657
onewire_bus;

.gitmodules

+3
Original file line numberDiff line numberDiff line change
@@ -57,3 +57,6 @@
5757
[submodule "catch2/Catch2"]
5858
path = catch2/Catch2
5959
url = https://github.com/catchorg/Catch2.git
60+
[submodule "l8w8jwt/l8w8jwt"]
61+
path = l8w8jwt/l8w8jwt
62+
url = https://github.com/GlitchedPolygons/l8w8jwt.git

l8w8jwt/CMakeLists.txt

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
idf_component_register(SRCS "l8w8jwt/src/base64.c"
2+
"l8w8jwt/src/claim.c"
3+
"l8w8jwt/src/decode.c"
4+
"l8w8jwt/src/encode.c"
5+
"l8w8jwt/src/util.c"
6+
"l8w8jwt/src/version.c"
7+
INCLUDE_DIRS "l8w8jwt/include"
8+
PRIV_INCLUDE_DIRS "port/private_include"
9+
PRIV_REQUIRES "mbedtls"
10+
)
11+
12+
set_source_files_properties("l8w8jwt/src/encode.c" "l8w8jwt/src/decode.c"
13+
PROPERTIES COMPILE_FLAGS "-Wno-maybe-uninitialized")
14+
15+
target_compile_options(${COMPONENT_LIB} PRIVATE "-DL8W8JWT_SMALL_STACK=1" "-DL8W8JWT_ENABLE_EDDSA=0")

l8w8jwt/LICENSE

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
l8w8jwt/LICENSE

l8w8jwt/README.md

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
l8w8jwt/README.md

l8w8jwt/examples/es256/CMakeLists.txt

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
# The following lines of boilerplate have to be in your project's
2+
# CMakeLists in this exact order for cmake to work correctly
3+
cmake_minimum_required(VERSION 3.16)
4+
5+
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
6+
project(es256)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
idf_component_register(SRCS "main.c"
2+
INCLUDE_DIRS "")
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
## IDF Component Manager Manifest File
2+
version: "1.0.0"
3+
description: l8w8jwt Example
4+
dependencies:
5+
idf: ">=5.0"
6+
espressif/l8w8jwt:
7+
version: '>=2.2.1'
8+
override_path: '../../../'

l8w8jwt/examples/es256/main/main.c

+166
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,166 @@
1+
/*
2+
Copyright 2020 Raphael Beck
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
#include <stdio.h>
18+
#include <string.h>
19+
#include "l8w8jwt/encode.h"
20+
#include "l8w8jwt/decode.h"
21+
22+
/*
23+
* This keypair was generated using the following command:
24+
* openssl ecparam -name prime256v1 -genkey -noout -out private.pem && openssl ec -in private.pem -pubout -out public.pem
25+
*/
26+
27+
static const char ECDSA_PRIVATE_KEY[] = "-----BEGIN EC PRIVATE KEY-----\n"
28+
"MHcCAQEEILvM6E7mLOdndALDyFc3sOgUTb6iVjgwRBtBwYZngSuwoAoGCCqGSM49\n"
29+
"AwEHoUQDQgAEMlFGAIxe+/zLanxz4bOxTI6daFBkNGyQ+P4bc/RmNEq1NpsogiMB\n"
30+
"5eXC7jUcD/XqxP9HCIhdRBcQHx7aOo3ayQ==\n"
31+
"-----END EC PRIVATE KEY-----";
32+
33+
static const char ECDSA_PUBLIC_KEY[] = "-----BEGIN PUBLIC KEY-----\n"
34+
"MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEMlFGAIxe+/zLanxz4bOxTI6daFBk\n"
35+
"NGyQ+P4bc/RmNEq1NpsogiMB5eXC7jUcD/XqxP9HCIhdRBcQHx7aOo3ayQ==\n"
36+
"-----END PUBLIC KEY-----";
37+
38+
int example_jwt_decode(char *jwt)
39+
{
40+
struct l8w8jwt_decoding_params params;
41+
l8w8jwt_decoding_params_init(&params);
42+
43+
params.alg = L8W8JWT_ALG_ES256;
44+
45+
params.jwt = jwt;
46+
params.jwt_length = strlen(jwt);
47+
48+
params.verification_key = (unsigned char *)ECDSA_PUBLIC_KEY;
49+
params.verification_key_length = strlen(ECDSA_PUBLIC_KEY);
50+
51+
params.validate_iss = "Black Mesa";
52+
params.validate_iss_length = strlen(params.validate_iss);
53+
54+
params.validate_sub = "Gordon Freeman";
55+
params.validate_sub_length = strlen(params.validate_sub);
56+
57+
params.validate_exp = 1;
58+
params.exp_tolerance_seconds = 60;
59+
60+
params.validate_iat = 1;
61+
params.iat_tolerance_seconds = 60;
62+
63+
enum l8w8jwt_validation_result validation_result;
64+
int r = l8w8jwt_decode(&params, &validation_result, NULL, NULL);
65+
66+
printf("\nl8w8jwt_decode_es256 function returned %s (code %d).\n\nValidation result: \n%d\n", r == L8W8JWT_SUCCESS ? "successfully" : "", r, validation_result);
67+
return r;
68+
}
69+
70+
char *example_jwt_encode(void)
71+
{
72+
char *jwt;
73+
size_t jwt_length;
74+
75+
struct l8w8jwt_claim header_claims[] = {
76+
{
77+
.key = "kid",
78+
.key_length = 3,
79+
.value = "some-key-id-here-012345",
80+
.value_length = strlen("some-key-id-here-012345"),
81+
.type = L8W8JWT_CLAIM_TYPE_STRING
82+
}
83+
};
84+
85+
struct l8w8jwt_claim payload_claims[] = {
86+
{
87+
.key = "ctx",
88+
.key_length = 3,
89+
.value = "Unforseen Consequences",
90+
.value_length = strlen("Unforseen Consequences"),
91+
.type = L8W8JWT_CLAIM_TYPE_STRING
92+
},
93+
{
94+
.key = "age",
95+
.key_length = 3,
96+
.value = "27",
97+
.value_length = strlen("27"),
98+
.type = L8W8JWT_CLAIM_TYPE_INTEGER
99+
},
100+
{
101+
.key = "size",
102+
.key_length = strlen("size"),
103+
.value = "1.85",
104+
.value_length = strlen("1.85"),
105+
.type = L8W8JWT_CLAIM_TYPE_NUMBER
106+
},
107+
{
108+
.key = "alive",
109+
.key_length = strlen("alive"),
110+
.value = "true",
111+
.value_length = strlen("true"),
112+
.type = L8W8JWT_CLAIM_TYPE_BOOLEAN
113+
},
114+
{
115+
.key = "nulltest",
116+
.key_length = strlen("nulltest"),
117+
.value = "null",
118+
.value_length = strlen("null"),
119+
.type = L8W8JWT_CLAIM_TYPE_NULL
120+
}
121+
};
122+
123+
struct l8w8jwt_encoding_params params;
124+
l8w8jwt_encoding_params_init(&params);
125+
126+
params.alg = L8W8JWT_ALG_ES256;
127+
128+
params.sub = "Gordon Freeman";
129+
params.sub_length = strlen("Gordon Freeman");
130+
131+
params.iss = "Black Mesa";
132+
params.iss_length = strlen("Black Mesa");
133+
134+
params.aud = "Administrator";
135+
params.aud_length = strlen("Administrator");
136+
137+
params.iat = time(NULL);
138+
params.exp = time(NULL) + 600; // Set to expire after 10 minutes (600 seconds).
139+
140+
params.additional_header_claims = header_claims;
141+
params.additional_header_claims_count = sizeof(header_claims) / sizeof(struct l8w8jwt_claim);
142+
143+
params.additional_payload_claims = payload_claims;
144+
params.additional_payload_claims_count = sizeof(payload_claims) / sizeof(struct l8w8jwt_claim);
145+
146+
params.secret_key = (unsigned char *)ECDSA_PRIVATE_KEY;
147+
params.secret_key_length = strlen(ECDSA_PRIVATE_KEY);
148+
149+
params.out = &jwt;
150+
params.out_length = &jwt_length;
151+
152+
int r = l8w8jwt_encode(&params);
153+
printf("\nl8w8jwt_encode_es256 function returned %s (code %d).\n\nCreated token: \n%s\n", r == L8W8JWT_SUCCESS ? "successfully" : "", r, jwt);
154+
155+
return jwt;
156+
}
157+
158+
void app_main(void)
159+
{
160+
printf("=== JWT Example on ESP32 ===\n");
161+
char *jwt = example_jwt_encode();
162+
int ret = example_jwt_decode(jwt);
163+
l8w8jwt_free(jwt); /* Never forget to free the jwt string! */
164+
printf("JWT generation and decoding: %s\n", ret == L8W8JWT_SUCCESS ? "success" : "failed");
165+
return;
166+
}

l8w8jwt/idf_component.yml

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
version: "2.2.1"
2+
description: "Minimal, OpenSSL-less and super lightweight JWT library written in C"
3+
url: https://github.com/espressif/idf-extra-components/tree/master/l8w8jwt
4+
dependencies:
5+
idf: ">=5.0"
6+
espressif/jsmn:
7+
version: "^1.1.0"

l8w8jwt/l8w8jwt

Submodule l8w8jwt added at 824bb52
+153
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
/*
2+
* SPDX-FileCopyrightText: 2020 Raphael Beck
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*
6+
* SPDX-FileContributor: 2023 Espressif Systems (Shanghai) CO LTD
7+
*/
8+
/*
9+
Copyright 2020 Raphael Beck
10+
11+
Licensed under the Apache License, Version 2.0 (the "License");
12+
you may not use this file except in compliance with the License.
13+
You may obtain a copy of the License at
14+
15+
http://www.apache.org/licenses/LICENSE-2.0
16+
17+
Unless required by applicable law or agreed to in writing, software
18+
distributed under the License is distributed on an "AS IS" BASIS,
19+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20+
See the License for the specific language governing permissions and
21+
limitations under the License.
22+
*/
23+
24+
/**
25+
* @file checknum.h
26+
* @author Raphael Beck
27+
* @brief Check whether a given string contains an integer or floating point number.
28+
*/
29+
30+
/* https://github.com/GlitchedPolygons/checknum */
31+
32+
#ifndef CHECKNUM_H
33+
#define CHECKNUM_H
34+
35+
#ifdef __cplusplus
36+
extern "C" {
37+
#endif
38+
39+
#ifdef CHECKNUM_STATIC
40+
#define CHECKNUM_API static
41+
#else
42+
#define CHECKNUM_API extern
43+
#endif
44+
45+
#include <string.h>
46+
#include <stddef.h>
47+
#include <stdbool.h>
48+
#include <inttypes.h>
49+
50+
/**
51+
* Checks whether a given string contains a valid integer or floating point number. <p>
52+
* If it's an integer, 1 is returned. <P>
53+
* If it's a float or a double, 2 is returned. <p>
54+
* If the string doesn't contain a valid number at all, 0 is returned.
55+
*/
56+
CHECKNUM_API int checknum(char *string, size_t string_length)
57+
{
58+
if (string == NULL) {
59+
return 0;
60+
}
61+
62+
if (string_length == 0) {
63+
string_length = strlen(string);
64+
}
65+
66+
char *c = string;
67+
68+
while (*c == ' ' && c < string + string_length) {
69+
c++;
70+
}
71+
72+
while (*(string + string_length - 1) == ' ' && c < string + string_length) {
73+
string_length--;
74+
}
75+
76+
switch (*c) {
77+
case '+':
78+
case '-':
79+
if (++c >= string + string_length) {
80+
return 0;
81+
}
82+
default:
83+
break;
84+
}
85+
86+
unsigned int type = 0;
87+
88+
if (*c == '0') {
89+
type |= 1 << 0;
90+
if (*++c != '.' && c < string + string_length) {
91+
return 0;
92+
}
93+
}
94+
95+
for (; c < string + string_length; c++) {
96+
switch (*c) {
97+
case '0':
98+
case '1':
99+
case '2':
100+
case '3':
101+
case '4':
102+
case '5':
103+
case '6':
104+
case '7':
105+
case '8':
106+
case '9':
107+
type |= 1 << 0;
108+
continue;
109+
case '-':
110+
if (type & 1 << 1 || (*(c - 1) != 'E' && *(c - 1) != 'e')) {
111+
return 0;
112+
}
113+
type |= 1 << 1;
114+
continue;
115+
case '.':
116+
if (type & 1 << 2 || type & 1 << 3) {
117+
return 0;
118+
}
119+
type |= 1 << 2;
120+
continue;
121+
case 'E':
122+
case 'e':
123+
if (!(type & 1 << 0) || type & 1 << 3 || c + 1 >= string + string_length) {
124+
return 0;
125+
}
126+
type |= 1 << 3;
127+
continue;
128+
case '+':
129+
if (type & 1 << 4 || (*(c - 1) != 'E' && *(c - 1) != 'e')) {
130+
return 0;
131+
}
132+
type |= 1 << 4;
133+
continue;
134+
default:
135+
return 0;
136+
}
137+
}
138+
139+
switch (type) {
140+
case 0:
141+
return 0;
142+
case 1 << 0:
143+
return 1;
144+
default:
145+
return type & 1 << 0 ? 2 : 0;
146+
}
147+
}
148+
149+
#ifdef __cplusplus
150+
} // extern "C"
151+
#endif
152+
153+
#endif // CHECKNUM_H

0 commit comments

Comments
 (0)