1 #!/usr/bin/env dub
2 /+ dub.sdl:
3     name "bench_l8w8jwt"
4     libs "l8w8jwt" "mbedcrypto" "mbedx509"
5 +/
6 
7 module benchmarks.l8w8jwt;
8 
9 import core.stdc.string;
10 import std.algorithm;
11 import std.array;
12 import std.conv;
13 import std.datetime.stopwatch;
14 import std.stdio;
15 import std.string;
16 
17 int main(string[] args)
18 {
19     // args: enc/dec/val, cycle count, alg, token/payload, signature
20     // output:
21     //   payload/token/true
22     //   msecs taken
23     //   GC used bytes
24 
25     if (args.length != 6) { writeln("Invalid args"); return 1; }
26     size_t cycles = args[2].to!size_t;
27 
28     int alg = args[3].predSwitch(
29         "HS256", L8W8JWT_ALG_HS256,
30         "HS384", L8W8JWT_ALG_HS384,
31         "HS512", L8W8JWT_ALG_HS512,
32         "RS256", L8W8JWT_ALG_RS256,
33         "RS384", L8W8JWT_ALG_RS384,
34         "RS512", L8W8JWT_ALG_RS512,
35         "ES256", L8W8JWT_ALG_ES256,
36         "ES384", L8W8JWT_ALG_ES384,
37         "ES512", L8W8JWT_ALG_ES512,
38         -1
39     );
40     if (alg < 0)
41     {
42         writeln("Unsupported algorithm");
43         return 1;
44     }
45 
46     {
47         StopWatch sw;
48         sw.start();
49         scope (exit)
50         {
51             sw.stop();
52             writeln(sw.peek.total!"msecs");
53             writeln(0); // C library, no GC used
54         }
55 
56         if (args[1] == "val") return validate(cycles, alg, args[4], args[5]);
57         else if (args[1] == "dec") return decode(cycles, alg, args[4], args[5]);
58         else if (args[1] == "enc") return encode(cycles, alg, args[4], args[5]);
59     }
60 
61     writeln("Invalid command: ", args[1]);
62     return 1;
63 }
64 
65 int validate(size_t cycles, int alg, string token, string secret)
66 {
67     l8w8jwt_decoding_params params;
68     l8w8jwt_decoding_params_init(&params);
69     params.alg = alg;
70     bool ret;
71     foreach (_; 0..cycles)
72     {
73         params.jwt = cast(char*)token.ptr;
74         params.jwt_length = token.length;
75 
76         params.verification_key = cast(ubyte*)secret.ptr;
77         params.verification_key_length = secret.length;
78 
79         l8w8jwt_validation_result validation_result;
80         immutable r = l8w8jwt_decode(&params, &validation_result, null, null);
81         ret = !r && validation_result == l8w8jwt_validation_result.L8W8JWT_VALID;
82     }
83 
84     writeln(ret);
85     return 0;
86 }
87 
88 int decode(size_t cycles, int alg, string token, string secret)
89 {
90     l8w8jwt_decoding_params params;
91     l8w8jwt_decoding_params_init(&params);
92     params.alg = alg;
93     size_t claims_length;
94     l8w8jwt_claim* claims;
95 
96     bool ret;
97     foreach (_; 0..cycles)
98     {
99         params.jwt = cast(char*)token.ptr;
100         params.jwt_length = token.length;
101 
102         params.verification_key = cast(ubyte*)secret.ptr;
103         params.verification_key_length = secret.length;
104 
105         l8w8jwt_validation_result validation_result;
106         if (claims) l8w8jwt_free_claims(claims, claims_length);
107         immutable r = l8w8jwt_decode(&params, &validation_result, &claims, &claims_length);
108         if (r || validation_result != l8w8jwt_validation_result.L8W8JWT_VALID)
109         {
110             writeln("Failed to decode token. r=", r, ", res=", validation_result);
111             return 1;
112         }
113     }
114 
115     chillbuff cb;
116     chillbuff_init(&cb, 32, 1, chillbuff_growth_method.CHILLBUFF_GROW_DUPLICATIVE);
117     assert(claims && claims_length > 1);
118     l8w8jwt_write_claims(&cb, claims+1, claims_length-1); // workaround - skip first as it has alg from header
119     l8w8jwt_free_claims(claims, claims_length);
120     scope (exit) chillbuff_free(&cb);
121     writeln('{', (cast(char*)cb.array)[0..cb.length], '}');
122 
123     return 0;
124 }
125 
126 int encode(size_t cycles, int alg, string payload, string secret)
127 {
128     // prep claims - not all data types handled properly, but enough for the test
129     import std.json, std.typecons;
130     auto jpay = parseJSON(payload);
131     auto cl = jpay.object.byKeyValue.map!(a => tuple(a.key, a.value.type.predSwitch(
132         JSONType..string, a.value.str,
133         JSONType.integer, a.value.integer.to!string
134     ), a.value.type == JSONType..string ? L8W8JWT_CLAIM_TYPE_STRING : L8W8JWT_CLAIM_TYPE_INTEGER)).array;
135 
136     l8w8jwt_claim[] payload_claims =
137         cl.map!(a => l8w8jwt_claim(
138             cast(char*)a[0].ptr,
139             a[0].length,
140             cast(char*)a[1].ptr,
141             a[1].length,
142             a[2]
143         )).array;
144 
145     char* jwt;
146     size_t jwt_length;
147     l8w8jwt_encoding_params params;
148     l8w8jwt_encoding_params_init(&params);
149     params.alg = alg;
150     params.secret_key = cast(ubyte*)secret.ptr;
151     params.secret_key_length = secret.length;
152     params.out_ = &jwt;
153     params.out_length = &jwt_length;
154 
155     foreach (_; 0..cycles)
156     {
157         if (jwt) l8w8jwt_free(jwt);
158         immutable r = l8w8jwt_encode(&params);
159         if (r)
160         {
161             writeln("Error encoding token: ", r);
162             return 1;
163         }
164     }
165 
166     scope (exit) l8w8jwt_free(jwt);
167     writeln(jwt[0..jwt_length]);
168     return 0;
169 }
170 
171 // bindings
172 extern(C) nothrow @nogc:
173 
174 import core.sys.posix.sys.select;
175 
176 enum L8W8JWT_ALG_HS256 = 0;
177 enum L8W8JWT_ALG_HS384 = 1;
178 enum L8W8JWT_ALG_HS512 = 2;
179 enum L8W8JWT_ALG_RS256 = 3;
180 enum L8W8JWT_ALG_RS384 = 4;
181 enum L8W8JWT_ALG_RS512 = 5;
182 enum L8W8JWT_ALG_PS256 = 6;
183 enum L8W8JWT_ALG_PS384 = 7;
184 enum L8W8JWT_ALG_PS512 = 8;
185 enum L8W8JWT_ALG_ES256 = 9;
186 enum L8W8JWT_ALG_ES384 = 10;
187 enum L8W8JWT_ALG_ES512 = 11;
188 enum L8W8JWT_ALG_ES256K = 12;
189 enum L8W8JWT_ALG_ED25519 = 13;
190 
191 enum l8w8jwt_validation_result
192 {
193     /**
194      * The JWT is valid (according to the passed validation parameters).
195      */
196     L8W8JWT_VALID = cast(uint) 0,
197 
198     /**
199      * The issuer claim is invalid.
200      */
201     L8W8JWT_ISS_FAILURE = cast(uint) 1 << cast(uint) 0,
202 
203     /**
204      * The subject claim is invalid.
205      */
206     L8W8JWT_SUB_FAILURE = cast(uint) 1 << cast(uint) 1,
207 
208     /**
209      * The audience claim is invalid.
210      */
211     L8W8JWT_AUD_FAILURE = cast(uint) 1 << cast(uint) 2,
212 
213     /**
214      * The JWT ID claim is invalid.
215      */
216     L8W8JWT_JTI_FAILURE = cast(uint) 1 << cast(uint) 3,
217 
218     /**
219      * The token is expired.
220      */
221     L8W8JWT_EXP_FAILURE = cast(uint) 1 << cast(uint) 4,
222 
223     /**
224      * The token is not yet valid.
225      */
226     L8W8JWT_NBF_FAILURE = cast(uint) 1 << cast(uint) 5,
227 
228     /**
229      * The token was not issued yet, are you from the future?
230      */
231     L8W8JWT_IAT_FAILURE = cast(uint) 1 << cast(uint) 6,
232 
233     /**
234      * The token was potentially tampered with: its signature couldn't be verified.
235      */
236     L8W8JWT_SIGNATURE_VERIFICATION_FAILURE = cast(uint) 1 << cast(uint) 7,
237 
238     /**
239      * The token's "typ" claim validation failed.
240      */
241     L8W8JWT_TYP_FAILURE = cast(uint) 1 << cast(uint) 8
242 }
243 
244 /**
245  * Struct containing the parameters to use for decoding and validating a JWT.
246  */
247 struct l8w8jwt_decoding_params
248 {
249     /**
250      * The token to decode and validate.
251      */
252     char* jwt;
253 
254     /**
255      * The jwt string length.
256      */
257     size_t jwt_length;
258 
259     /**
260      * The signature algorithm ID. <p>
261      * [0;2] = HS256/384/512 | [3;5] = RS256/384/512 | [6;8] = PS256/384/512 | [9;11] = ES256/384/512 <p>
262      * This affects what should be the value of {@link #verification_key}
263      */
264     int alg;
265 
266     /**
267      * [OPTIONAL] The issuer claim (who issued the JWT?). <p>
268      * Set to <code>NULL</code> if you don't want to validate the issuer. <p>
269      * The JWT will only pass verification if its <code>iss</code> claim matches this string.
270      * @see https://tools.ietf.org/html/rfc7519#section-4.1.1
271      */
272     char* validate_iss;
273 
274     /**
275      * validate_iss string length.
276      */
277     size_t validate_iss_length;
278 
279     /**
280      * [OPTIONAL] The subject claim (who is the JWT about?). <p>
281      * Set to <code>NULL</code> if you don't want to validate the subject claim. <p>
282      * The JWT will only pass verification if its <code>sub</code> matches this string.
283      * @see https://tools.ietf.org/html/rfc7519#section-4.1.2
284      */
285     char* validate_sub;
286 
287     /**
288      * validate_sub string length.
289      */
290     size_t validate_sub_length;
291 
292     /**
293      * [OPTIONAL] The audience claim (who is the JWT intended for? Who is the intended JWT's recipient?). <p>
294      * Set to <code>NULL</code> if you don't want to validate the audience. <p>
295      * The JWT will only pass verification if its <code>aud</code> matches this string.
296      * @see https://tools.ietf.org/html/rfc7519#section-4.1.3
297      */
298     char* validate_aud;
299 
300     /**
301      * validate_aud string length.
302      */
303     size_t validate_aud_length;
304 
305     /**
306      * [OPTIONAL] The JWT ID. Provides a unique identifier for the token. <p>
307      * Set to <code>NULL</code> if you don't want to validate the jti claim. <p>
308      * The JWT will only pass verification if its <code>jti</code> matches this string.
309      * @see https://tools.ietf.org/html/rfc7519#section-4.1.7
310      */
311     char* validate_jti;
312 
313     /**
314      * validate_jti claim length.
315      */
316     size_t validate_jti_length;
317 
318     /**
319      * Should the expiration claim be verified?
320      * If this is set to <code>1</code>, the <code>exp</code> claim will be compared to the current date and time + {@link #exp_tolerance_seconds}
321      */
322     int validate_exp;
323 
324     /**
325      * Should the "not before" claim be verified?
326      * If this is set to <code>1</code>, the <code>nbf</code> claim will be compared to the current date and time + {@link #nbf_tolerance_seconds}
327      */
328     int validate_nbf;
329 
330     /**
331      * Should the "issued at" claim be verified?
332      * If this is set to <code>1</code>, the <code>iat</code> claim will be compared to the current date and time + {@link #iat_tolerance_seconds}
333      */
334     int validate_iat;
335 
336     /**
337      * Small inconsistencies in time can happen, or also latency between clients and servers.
338      * That's just life. You can forgive a few seconds of expiration, but don't exaggerate this! <p>
339      * Only taken into consideration if {@link #validate_exp} is set to <code>1</code>.
340      */
341     ubyte exp_tolerance_seconds;
342 
343     /**
344      * The amount of seconds to subtract from the current time when comparing the "not before" claim, to allow for a small tolerance time frame.
345      * Only taken into consideration if {@link #validate_nbf} is set to <code>1</code>.
346      */
347     ubyte nbf_tolerance_seconds;
348 
349     /**
350      * The amount of seconds to subtract from the current time when comparing the "issued at" claim, to allow for a small tolerance time frame.
351      * Only taken into consideration if {@link #validate_iat} is set to <code>1</code>.
352      */
353     ubyte iat_tolerance_seconds;
354 
355     /**
356      * The key to use for verifying the token's signature
357      * (e.g. if you chose HS256 as algorithm, this will be the HMAC secret; for RS512 this will be the PEM-formatted public RSA key string, etc...).
358      */
359     ubyte* verification_key;
360 
361     /**
362      * Length of the {@link #verification_key}
363      */
364     size_t verification_key_length;
365 
366     /**
367      * [OPTIONAL] The typ claim (what type is the token?). <p>
368      * Set to <code>NULL</code> if you don't want to validate the "typ" claim. <p>
369      */
370     char* validate_typ;
371 
372     /**
373      * validate_typ string length.
374      */
375     size_t validate_typ_length;
376 }
377 
378 /**
379  * Initializes a {@link #l8w8jwt_decoding_params} instance by setting its fields to default values.
380  * @param params The l8w8jwt_decoding_params to initialize (set to default values).
381  */
382 void l8w8jwt_decoding_params_init (l8w8jwt_decoding_params* params);
383 
384 /**
385  * Validates a set of l8w8jwt_decoding_params.
386  * @param params The l8w8jwt_decoding_params to validate.
387  * @return Return code as defined in retcodes.h
388  */
389 int l8w8jwt_validate_decoding_params (l8w8jwt_decoding_params* params);
390 
391 /**
392  * Decode (and validate) a JWT using specific parameters. <p>
393  * The resulting {@link #l8w8jwt_validation_result} written into the passed "out_validation_result" pointer
394  * contains validation failure flags (see the {@link #l8w8jwt_validation_result} enum docs for more details). <p>
395  * This only happens if decoding also succeeded: if the token is malformed, nothing will be written into "out_validation_result". <p>
396  * If validation succeeds, the {@link #l8w8jwt_validation_result} receives the value 0 (enum value <code>L8W8JWT_VALID</code>). <p>
397  * The same applies to the "out_claims" argument: it is only allocated and written to if it (obviously) isn't <code>NULL</code> and if the decoding was also successful!
398  *
399  * @param params The parameters to use for decoding and validating the token.
400  *
401  * @param out_validation_result Where to write the validation result flags into (0 means success). In case of a decoding failure this is set to -1 (or <code>~L8W8JWT_VALID</code>)!
402  *
403  * @param out_claims
404  * [OPTIONAL] Where the decoded claims (header + payload claims together) should be written into.
405  * This pointer will be dereferenced + allocated, so make sure to pass a fresh pointer!
406  * If you don't need the claims, set this to <code>NULL</code> (they will only be validated, e.g. signature, exp, etc...).
407  * Check the note down below for more infos!
408  *
409  * @param out_claims_length Where to write the decoded claims count into. This will receive the value of how many claims were written into "out_claims" (0 if you decided to set "out_claims" to <code>NULL</code>).
410  *
411  * @note If you decide to keep the claims stored in the <code>out_claims</code> parameter, REMEMBER to call {@link #l8w8jwt_free_claims()} on it once you're done using them!
412  *
413  * @return Return code as defined in retcodes.h (this is NOT the validation result that's written into the out_validation_result argument; the returned int describes whether the actual parsing/decoding part failed).
414  */
415 int l8w8jwt_decode (l8w8jwt_decoding_params* params, l8w8jwt_validation_result* out_validation_result, l8w8jwt_claim** out_claims, size_t* out_claims_length);
416 enum L8W8JWT_MAX_KEY_SIZE = 8192;
417 
418 /**
419  * JWT claim value is a string (e.g. <code>"iss": "glitchedpolygons.com"</code>).
420  */
421 enum L8W8JWT_CLAIM_TYPE_STRING = 0;
422 
423 /**
424  * JWT claim value is an integer (e.g. <code>"exp": 1579610629</code>)
425  */
426 enum L8W8JWT_CLAIM_TYPE_INTEGER = 1;
427 
428 /**
429  * JWT claim value type number (e.g. <code>"size": 1.85</code>).
430  */
431 enum L8W8JWT_CLAIM_TYPE_NUMBER = 2;
432 
433 /**
434  * JWT claim value is a boolean (e.g. <code>"done": true</code>).
435  */
436 enum L8W8JWT_CLAIM_TYPE_BOOLEAN = 3;
437 
438 /**
439  * JWT claim value is null (e.g. <code>"ref": null</code>).
440  */
441 enum L8W8JWT_CLAIM_TYPE_NULL = 4;
442 
443 /**
444  * JWT claim value type JSON array (e.g. <code>"ids": [2, 4, 8, 16]</code>).
445  */
446 enum L8W8JWT_CLAIM_TYPE_ARRAY = 5;
447 
448 /**
449  * JWT claim value type is a JSON object (e.g. <code>"objs": { "name": "GMan", "id": 420 }</code>).
450  */
451 enum L8W8JWT_CLAIM_TYPE_OBJECT = 6;
452 
453 /**
454  * JWT claim value is some other type.
455  */
456 enum L8W8JWT_CLAIM_TYPE_OTHER = 7;
457 
458 /**
459  * Struct containing a jwt claim key-value pair.<p>
460  * If allocated on the heap by the decode function,
461  * remember to call <code>l8w8jwt_claims_free()</code> on it once you're done using it.
462  */
463 struct l8w8jwt_claim
464 {
465     /**
466      * The token claim key (e.g. "iss", "iat", "sub", etc...). <p>
467      * NUL-terminated C-string!
468      */
469     char* key;
470 
471     /**
472      * key string length. <p>
473      * Set this to <code>0</code> if you want to make the encoder use <code>strlen(key)</code> instead.
474      */
475     size_t key_length;
476 
477     /**
478      * The claim's value as a NUL-terminated C-string.
479      */
480     char* value;
481 
482     /**
483      * value string length. <p>
484      * Set this to <code>0</code> if you want to make the encoder use <code>strlen(value)</code> instead.
485      */
486     size_t value_length;
487 
488     /**
489      * The type of the claim's value. <p>
490      * 0 = string, 1 = integer, 2 = number, 3 = boolean, 4 = null, 5 = array, 6 = object, 7 = other.
491      * @see https://www.w3schools.com/js/js_json_datatypes.asp
492      */
493     int type;
494 }
495 
496 /**
497  * Frees a heap-allocated <code>l8w8jwt_claim</code> array.
498  * @param claims The claims to free.
499  * @param claims_count The size of the passed claims array.
500  */
501 void l8w8jwt_free_claims (l8w8jwt_claim* claims, size_t claims_count);
502 
503 /**
504  * Writes a bunch of JWT claims into a chillbuff stringbuilder. <p>
505  * Curly braces and trailing commas won't be written; only the "key":"value" pairs!
506  * @param stringbuilder The buffer into which to write the claims.
507  * @param claims The l8w8jwt_claim array of claims to write.
508  * @param claims_count The claims array size.
509  * @return Return code as specified inside retcodes.h
510  */
511 int l8w8jwt_write_claims (chillbuff* stringbuilder, l8w8jwt_claim* claims, size_t claims_count);
512 
513 /**
514  * Gets a claim by key from a l8w8jwt_claim array.
515  * @param claims The array to look in.
516  * @param claims_count The claims array size.
517  * @param key The claim key (e.g. "sub") to look for.
518  * @param key_length The claim key's string length.
519  * @return The found claim; <code>NULL</code> if no such claim was found in the array.
520  */
521 l8w8jwt_claim* l8w8jwt_get_claim (l8w8jwt_claim* claims, size_t claims_count, const(char)* key, size_t key_length);
522 
523 /**
524  * Struct containing the parameters to use for creating a JWT with l8w8jwt.
525  */
526 struct l8w8jwt_encoding_params
527 {
528     /**
529      * The signature algorithm ID. <p>
530      * [0;2] = HS256/384/512 | [3;5] = RS256/384/512 | [6;8] = PS256/384/512 | [9;11] = ES256/384/512
531      */
532     int alg;
533 
534     /**
535      * [OPTIONAL] The issuer claim (who issued the JWT?). Can be omitted by setting this to <code>NULL</code>.
536      * @see https://tools.ietf.org/html/rfc7519#section-4.1.1
537      */
538     char* iss;
539 
540     /**
541      * iss claim string length.
542      */
543     size_t iss_length;
544 
545     /**
546      * [OPTIONAL] The subject claim (who is the JWT about?). Set to <code>NULL</code> if you don't want it in your token.
547      * @see https://tools.ietf.org/html/rfc7519#section-4.1.2
548      */
549     char* sub;
550 
551     /**
552      * sub claim string length.
553      */
554     size_t sub_length;
555 
556     /**
557      * [OPTIONAL] The audience claim (who is the JWT intended for? Who is the intended JWT's recipient?).
558      * Set this to <code>NULL</code> if you don't wish to add this claim to the token.
559      * @see https://tools.ietf.org/html/rfc7519#section-4.1.3
560      */
561     char* aud;
562 
563     /**
564      * aud claim string length.
565      */
566     size_t aud_length;
567 
568     /**
569      * [OPTIONAL] The JWT ID. Provides a unique identifier for the token. Can be omitted by setting this to <code>NULL</code>.
570      * @see https://tools.ietf.org/html/rfc7519#section-4.1.7
571      */
572     char* jti;
573 
574     /**
575      * jti claim string length.
576      */
577     size_t jti_length;
578 
579     /**
580      * Expiration time claim; specifies when this token should stop being valid (in seconds since Unix epoch). <p>
581      * If you want to omit this, set this to <code>0</code>, but do NOT FORGET to set it to something,
582      * otherwise it will be set to whatever random value was in the memory where this variable resides.
583      * @see https://tools.ietf.org/html/rfc7519#section-4.1.4
584      */
585     time_t exp;
586 
587     /**
588      * "Not before" time claim; specifies when this token should start being valid (in seconds since Unix epoch). <p>
589      * If you want to omit this, set this to <code>0</code>, but do NOT FORGET to set it to something,
590      * otherwise it will be set to whatever random value was in the memory where this variable resides.
591      * @see https://tools.ietf.org/html/rfc7519#section-4.1.5
592      */
593     time_t nbf;
594 
595     /**
596      * "Issued at" timestamp claim; specifies when this token was emitted (in seconds since Unix epoch). <p>
597      * If you want to omit this, set this to <code>0</code>, but do NOT FORGET to set it to something,
598      * otherwise it will be set to whatever random value was in the memory where this variable resides.
599      * @see https://tools.ietf.org/html/rfc7519#section-4.1.6
600      */
601     time_t iat;
602 
603     /**
604      * [OPTIONAL] Array of additional claims to include in the JWT's header like for example "kid" or "cty"; pass <code>NULL</code> if you don't wish to add any! <p>
605      * Avoid header claims such as <code>typ</code> and <code>alg</code>, since those are written by the encoding function itself.
606      * @see https://tools.ietf.org/html/rfc7519#section-4.1.7
607      */
608     l8w8jwt_claim* additional_header_claims;
609 
610     /**
611      * [OPTIONAL] The additional_header_claims array size; pass <code>0</code> if you don't wish to include any custom claims!
612      */
613     size_t additional_header_claims_count;
614 
615     /**
616      * [OPTIONAL] Array of additional claims to include in the JWT's payload; pass <code>NULL</code> if you don't wish to add any! <p>
617      * Registered claim names such as "iss", "exp", etc... have their own dedicated field within this struct: do not include those in this array to prevent uncomfortable duplicates!
618      * @see https://tools.ietf.org/html/rfc7519#section-4
619      */
620     l8w8jwt_claim* additional_payload_claims;
621 
622     /**
623      * [OPTIONAL] The additional_payload_claims array size; pass <code>0</code> if you don't wish to include any custom claims!
624      */
625     size_t additional_payload_claims_count;
626 
627     /**
628      * The secret key to use for signing the token
629      * (e.g. if you chose HS256 as algorithm, this will be the HMAC secret; for RS512 this will be the private PEM-formatted RSA key string, and so on...).
630      */
631     ubyte* secret_key;
632 
633     /**
634      * Length of the secret_key
635      */
636     size_t secret_key_length;
637 
638     /**
639      * If the secret key requires a password for usage, please assign it to this field. <p>
640      * You can only omit this when using JWT algorithms "HS256", "HS384" or "HS512" (it's ignored in that case actually). <p>
641      * Every other algorithm requires you to at least set this to <code>NULL</code> if the {@link #secret_key} isn't password-protected.
642      */
643     ubyte* secret_key_pw;
644 
645     /**
646      * The secret key's password length (if there is any). If there's none, set this to zero!
647      */
648     size_t secret_key_pw_length;
649 
650     /**
651      * Where the encoded token should be written into
652      * (will be malloc'ed, so make sure to <code>l8w8jwt_free()</code> this as soon as you're done using it!).
653      */
654     char** out_;
655 
656     /**
657      * Where the output token string length should be written into.
658      */
659     size_t* out_length;
660 }
661 
662 /**
663  * Initializes a {@link #l8w8jwt_encoding_params} instance by setting its fields to default values.
664  * @param params The l8w8jwt_encoding_params to initialize (set to default values).
665  */
666 void l8w8jwt_encoding_params_init (l8w8jwt_encoding_params* params);
667 
668 /**
669  * Validates a set of l8w8jwt_encoding_params.
670  * @param params The l8w8jwt_encoding_params to validate.
671  * @return Return code as defined in retcodes.h
672  */
673 int l8w8jwt_validate_encoding_params (l8w8jwt_encoding_params* params);
674 
675 /**
676  * Creates, signs and encodes a Json-Web-Token. <p>
677  * An example output could be: <code>eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6InNvbWUta2V5LWlkLWhlcmUtMDEyMzQ1NiJ9.eyJpYXQiOjE1Nzk2NDUzNTUsImV4cCI6MTU3OTY0NTk1NSwic3ViIjoiR29yZG9uIEZyZWVtYW4iLCJpc3MiOiJCbGFjayBNZXNhIiwiYXVkIjoiQWRtaW5pc3RyYXRvciJ9.uk4EEoq0ql_SguLto5EWzklakpzO-6GE2U26crB8vUY</code> <p>
678  * @param params The token encoding parameters (e.g. "alg", "iss", "exp", etc...).
679  * @return Return code as defined in retcodes.h
680  * @see l8w8jwt_encoding_params
681  */
682 int l8w8jwt_encode (l8w8jwt_encoding_params* params);
683 
684 void l8w8jwt_free(void* mem);
685 
686 // chillbuff bindings
687 
688 /**
689  * How should the chillbuff's underlying array grow in size
690  * once its maximum capacity is reached during a push_back?
691  */
692 enum chillbuff_growth_method
693 {
694     /**
695      * Double the capacity.
696      */
697     CHILLBUFF_GROW_DUPLICATIVE = 0,
698 
699     /**
700      * Triple the capacity.
701      */
702     CHILLBUFF_GROW_TRIPLICATIVE = 1,
703 
704     /**
705      * Grow by the same capacity every time the buffer is full.
706      */
707     CHILLBUFF_GROW_LINEAR = 2,
708 
709     /**
710      * Multiplies the capacity by itself. Not the greatest idea... Use carefully!
711      */
712     CHILLBUFF_GROW_EXPONENTIAL = 3
713 }
714 
715 /**
716  * Self-reallocating dynamic size array of no strictly defined type.
717  * Easy 'n' "chill" (hope you like segmentation fault errors).
718  */
719 struct chillbuff
720 {
721     /**
722      * The buffer's underlying array that stores the data.
723      */
724     void* array;
725 
726     /**
727      * The current amount of elements stored in the chillbuff. DO NOT touch this yourself, only read!
728      */
729     size_t length;
730 
731     /**
732      * The current buffer capacity. This grows dynamically according to the specified {@link #chillbuff_growth_method}.
733      */
734     size_t capacity;
735 
736     /**
737      * The size of each stored element. DO NOT CHANGE THIS! Only read (if necessary)...
738      */
739     size_t element_size;
740 
741     /**
742      * The way the buffer's capacity is increased when it's full.
743      */
744     chillbuff_growth_method growth_method;
745 }
746 
747 /**
748  * Initializes a chillbuff instance and makes it ready to accept data.
749  * @param buff The chillbuff instance to init (or rather, a pointer to it).
750  * @param initial_capacity The initial capacity of the underlying array. If you pass <code>0</code> here, <code>16</code> is used by default.
751  * @param element_size How big should every array element be? E.g. if you're storing <code>int</code> you should pass <code>sizeof(int)</code>.
752  * @param growth_method How should the buffer grow once its maximum capacity is reached? @see chillbuff_growth_method
753  * @return Chillbuff exit code as defined at the top of the chillbuff.h header file. <code>0</code> means success.
754  */
755 int chillbuff_init (
756     chillbuff* buff,
757     const size_t initial_capacity,
758     const size_t element_size,
759     const chillbuff_growth_method growth_method)
760 {
761     import core.stdc.stdlib;
762     buff.array = malloc(min(16, initial_capacity)*element_size);
763     buff.capacity = min(16, initial_capacity);
764     buff.element_size = element_size;
765     buff.growth_method = growth_method;
766     return 0;
767 }
768 
769 /**
770  * Frees a chillbuff instance.
771  * @param buff The chillbuff to deallocate. If this is <code>NULL</code>, nothing happens at all.
772  */
773 void chillbuff_free (chillbuff* buff)
774 {
775     import core.stdc.stdlib;
776     free(buff.array);
777 }
778 
779 // /**
780 //  * Clears a chillbuff's data. <p>
781 //  * Deletes all of the underlying array's elements and resets the length to <code>0</code>. <p>
782 //  * Leaves the array allocated at the current capacity.
783 //  * @param buff The chillbuff to clear. If this is <code>NULL</code>, nothing happens at all.
784 //  */
785 // void chillbuff_clear (chillbuff* buff);