// Local functions...
//
+static cups_json_t *find_key(cups_json_t *jwk, cups_jwa_t sigalg, const char *kid);
#ifdef HAVE_OPENSSL
static BIGNUM *make_bignum(cups_json_t *jwk, const char *key);
static void make_bnstring(const BIGNUM *bn, char *buffer, size_t bufsize);
if (!jwt || !jwt->signature || !jwk)
return (false);
- DEBUG_printf("1cupsJWTHasValidSignature: orig sig(%u) = %02X%02X%02X%02X...%02X%02X%02X%02X", (unsigned)jwt->sigsize, jwt->signature[0], jwt->signature[1], jwt->signature[2], jwt->signature[3], jwt->signature[jwt->sigsize - 4], jwt->signature[jwt->sigsize - 3], jwt->signature[jwt->sigsize - 2], jwt->signature[jwt->sigsize - 1]);
+ DEBUG_printf("1cupsJWTHasValidSignature: sigalg=%d, orig sig[%u]=<%02X%02X%02X%02X...%02X%02X%02X%02X>", jwt->sigalg, (unsigned)jwt->sigsize, jwt->signature[0], jwt->signature[1], jwt->signature[2], jwt->signature[3], jwt->signature[jwt->sigsize - 4], jwt->signature[jwt->sigsize - 3], jwt->signature[jwt->sigsize - 2], jwt->signature[jwt->sigsize - 1]);
switch (jwt->sigalg)
{
// Get the message hash...
text = make_string(jwt, false);
text_len = strlen(text);
+ jwk = find_key(jwk, jwt->sigalg, jwt->sigkid);
#ifdef HAVE_OPENSSL
hash_len = cupsHashData(cups_jwa_algorithms[jwt->sigalg], text, text_len, hash, sizeof(hash));
// Get the message hash...
text = make_string(jwt, false);
text_len = strlen(text);
+ jwk = find_key(jwk, jwt->sigalg, jwt->sigkid);
#ifdef HAVE_OPENSSL
hash_len = cupsHashData(cups_jwa_algorithms[jwt->sigalg], text, text_len, hash, sizeof(hash));
cups_jwt_t *jwt; // JWT object
size_t datalen; // Size of data
char data[65536]; // Data
- const char *alg; // Signature algorithm, if any
+ const char *kid, // Key identifier
+ *alg; // Signature algorithm, if any
// Allocate a JWT...
// Import JSON...
cups_json_t *json, // JSON data
*json_value, // BASE64URL-encoded string value node
- *header, // Unprotected header
- *kid, // Key ID node
*signatures, // Signatures array
*signature; // Signature element to load
const char *value, // C string value
memcpy(jwt->signature, data, datalen);
jwt->sigsize = datalen;
}
-
- if ((header = cupsJSONFind(signature, "header")) != NULL && (kid = cupsJSONFind(header, "kid")) != NULL && (value = cupsJSONGetString(kid)) != NULL)
- jwt->sigkid = strdup(value);
}
+#ifdef DEBUG
+ if (jwt->sigsize >= 8)
+ DEBUG_printf("1cupsJWTImportString: signature[%u]=<%02X%02X%02X%02X...%02X%02X%02X%02X>", (unsigned)jwt->sigsize, jwt->signature[0], jwt->signature[1], jwt->signature[2], jwt->signature[3], jwt->signature[jwt->sigsize - 4], jwt->signature[jwt->sigsize - 3], jwt->signature[jwt->sigsize - 2], jwt->signature[jwt->sigsize - 1]);
+ else if (jwt->sigsize > 0)
+ DEBUG_printf("1cupsJWTImportString: signature[%u]=<...>", (unsigned)jwt->sigsize);
+#endif // DEBUG
+
// Check the algorithm used in the protected header...
if ((alg = cupsJSONGetString(cupsJSONFind(jwt->jose, "alg"))) != NULL)
{
cups_jwa_t sigalg; // Signing algorithm
+ DEBUG_printf("1cupsJWTImportString: alg=\"%s\"", alg);
+
for (sigalg = CUPS_JWA_NONE; sigalg < CUPS_JWA_MAX; sigalg ++)
{
if (!strcmp(alg, cups_jwa_strings[sigalg]))
{
jwt->sigalg = sigalg;
+ DEBUG_printf("1cupsJWTImportString: sigalg=%d", sigalg);
break;
}
}
}
+ if ((kid = cupsJSONGetString(cupsJSONFind(jwt->jose, "kid"))) != NULL)
+ {
+ DEBUG_printf("1cupsJWTImportString: kid=\"%s\"", kid);
+ jwt->sigkid = strdup(kid);
+ }
+
// Can't have signature with none or no signature for !none...
if ((jwt->sigalg == CUPS_JWA_NONE) != (jwt->sigsize == 0))
goto import_error;
// Create new signature...
if (!make_signature(jwt, alg, jwk, signature, &sigsize, &sigkid))
+ {
+ DEBUG_puts("2cupsJWTSign: Unable to create signature.");
return (false);
+ }
if (sigkid)
jwt->sigkid = strdup(sigkid);
if ((jwt->signature = malloc(sigsize)) == NULL)
+ {
+ DEBUG_printf("2cupsJWTSign: Unable to allocate %d bytes for signature.", (int)sigsize);
return (false);
+ }
memcpy(jwt->signature, signature, sigsize);
jwt->sigalg = alg;
}
+//
+// 'find_key()' - Find the key by name or algorithm.
+//
+
+static cups_json_t * // O - Key data
+find_key(cups_json_t *jwk, // I - Key set
+ cups_jwa_t alg, // I - Signature algorithm
+ const char *kid) // I - Signature key ID
+{
+ cups_json_t *keys; // Array of keys
+
+
+ if ((keys = cupsJSONFind(jwk, "keys")) != NULL)
+ {
+ // Full key set, find the key we need to use...
+ size_t i, // Looping var
+ count; // Number of keys
+ cups_json_t *current; // Current key
+ const char *curkid, // Current key ID
+ *curkty; // Current key type
+
+ count = cupsJSONGetCount(keys);
+
+ if (kid)
+ {
+ // Find the matching key ID
+ for (i = 0; i < count; i ++)
+ {
+ current = cupsJSONGetChild(keys, i);
+ curkid = cupsJSONGetString(cupsJSONFind(current, "kid"));
+
+ if (curkid && !strcmp(curkid, kid))
+ {
+ DEBUG_printf("4make_signature: Found matching key \"%s\" at %p.", curkid, (void *)current);
+ jwk = current;
+ break;
+ }
+ }
+ }
+ else
+ {
+ // Find a key that can be used for the specified algorithm
+ for (i = 0; i < count; i ++)
+ {
+ current = cupsJSONGetChild(keys, i);
+ curkty = cupsJSONGetString(cupsJSONFind(current, "kty"));
+
+ if (((!curkty || !strcmp(curkty, "ocy")) && alg >= CUPS_JWA_HS256 && alg <= CUPS_JWA_HS512) || (curkty && !strcmp(curkty, "RSA") && alg >= CUPS_JWA_RS256 && alg <= CUPS_JWA_RS512) || (curkty && !strcmp(curkty, "EC") && alg >= CUPS_JWA_ES256 && alg <= CUPS_JWA_ES512))
+ {
+ DEBUG_printf("4make_signature: Found compatible key \"%s\" at %p.", cupsJSONGetString(cupsJSONFind(current, "kid")), (void *)current);
+ jwk = current;
+ break;
+ }
+ }
+ }
+ }
+
+ return (jwk);
+}
+
+
#ifdef HAVE_OPENSSL
//
// 'make_bignum()' - Make a BIGNUM for the specified key.
const char **sigkid) // IO - Key ID string, if any
{
bool ret = false; // Return value
- cups_json_t *keys; // Array of keys
char *text; // JWS Signing Input
size_t text_len; // Length of signing input
#ifdef HAVE_OPENSSL
#endif // HAVE_OPENSSL
+ DEBUG_printf("3make_signature(jwt=%p, alg=%d, jwk=%p, signature=%p, sigsize=%p(%u), sigkid=%p(%s))", (void *)jwt, alg, (void *)jwk, (void *)signature, (void *)sigsize, (unsigned)*sigsize, (void *)sigkid, *sigkid);
+
// Get text to sign...
text = make_string(jwt, false);
text_len = strlen(text);
-
- if ((keys = cupsJSONFind(jwk, "keys")) != NULL)
- {
- // Full key set, find the key we need to use...
- size_t i, // Looping var
- count; // Number of keys
- cups_json_t *current; // Current key
- const char *curkid, // Current key ID
- *curkty; // Current key type
-
- count = cupsJSONGetCount(keys);
-
- if (*sigkid)
- {
- // Find the matching key ID
- for (i = 0; i < count; i ++)
- {
- current = cupsJSONGetChild(keys, i);
- curkid = cupsJSONGetString(cupsJSONFind(current, "kid"));
-
- if (curkid && !strcmp(curkid, *sigkid))
- {
- jwk = current;
- break;
- }
- }
- }
- else
- {
- // Find a key that can be used for the specified algorithm
- for (i = 0; i < count; i ++)
- {
- current = cupsJSONGetChild(keys, i);
- curkty = cupsJSONGetString(cupsJSONFind(current, "kty"));
-
- if (((!curkty || !strcmp(curkty, "ocy")) && alg >= CUPS_JWA_HS256 && alg <= CUPS_JWA_HS512) || (curkty && !strcmp(curkty, "RSA") && alg >= CUPS_JWA_RS256 && alg <= CUPS_JWA_RS512) || (curkty && !strcmp(curkty, "EC") && alg >= CUPS_JWA_ES256 && alg <= CUPS_JWA_ES512))
- {
- jwk = current;
- break;
- }
- }
- }
- }
+ jwk = find_key(jwk, alg, *sigkid);
if (alg >= CUPS_JWA_HS256 && alg <= CUPS_JWA_HS512)
{
size_t key_len; // Length of key
ssize_t hmac_len; // Length of HMAC
+ DEBUG_puts("4make_signature: HMAC signature");
+
// Get key...
memset(key, 0, sizeof(key));
k = cupsJSONGetString(cupsJSONFind(jwk, "k"));
else if (alg >= CUPS_JWA_RS256 && alg <= CUPS_JWA_RS512)
{
// RSASSA-PKCS1-v1_5 SHA-256/384/512
+ DEBUG_puts("4make_signature: RSA signature");
+
#ifdef HAVE_OPENSSL
unsigned char hash[128]; // SHA-256/384/512 hash
ssize_t hash_len; // Length of hash
else if (alg >= CUPS_JWA_ES256 && alg <= CUPS_JWA_ES512)
{
// ECDSA P-256 SHA-256/384/512
+ DEBUG_puts("4make_signature: ECDSA signature");
+
static unsigned sig_sizes[3] = // Sizes of signatures
{ 64, 96, 132 };
#ifdef HAVE_OPENSSL
gnutls_free(r.data);
gnutls_free(s.data);
}
-
+ else
+ {
+ DEBUG_printf("4make_signature: EC signing failed, sig_datum=%d bytes.", (int)sig_datum.size);
+ }
gnutls_free(sig_datum.data);
gnutls_privkey_deinit(key);
}
done:
+ DEBUG_printf("4make_signature: Returning %s.", ret ? "true" : "false");
+
free(text);
if (ret)
else
{
// Try loading JWT string on the command-line...
+ cups_json_t *jwks = NULL; // JWT Key Set, if any
+
for (i = 1; i < argc; i ++)
{
- if ((jwt = cupsJWTImportString(argv[i], CUPS_JWS_FORMAT_COMPACT)) != NULL)
+ if (!access(argv[i], R_OK))
+ {
+ if ((jwks = cupsJSONImportFile(argv[i])) == NULL)
+ {
+ fprintf(stderr, "%s: %s\n", argv[i], cupsGetErrorString());
+ return (1);
+ }
+ }
+ else if ((jwt = cupsJWTImportString(argv[i], CUPS_JWS_FORMAT_COMPACT)) != NULL)
{
-// printf("%s: OK, %u key/value pairs in root object.\n", argv[i], (unsigned)(cupsJSONGetCount(json) / 2));
+ cups_json_t *claims = cupsJWTGetClaims(jwt);
+ // All claims
+ cups_json_t *headers = cupsJWTGetHeaders(jwt);
+ // All JOSE headers
+ char *temp; // Temporary string
+ const char *aud = cupsJWTGetClaimString(jwt, CUPS_JWT_AUD);
+ // Audience
+ const char *iss = cupsJWTGetClaimString(jwt, CUPS_JWT_ISS);
+ // Issuer
+ const char *jti = cupsJWTGetClaimString(jwt, CUPS_JWT_JTI);
+ // JWT ID
+ const char *name = cupsJWTGetClaimString(jwt, CUPS_JWT_NAME);
+ // Display name
+ const char *sub = cupsJWTGetClaimString(jwt, CUPS_JWT_SUB);
+ // Subject (username/ID)
+ double iat = cupsJWTGetClaimNumber(jwt, CUPS_JWT_IAT);
+ // Issue time
+ double exp = cupsJWTGetClaimNumber(jwt, CUPS_JWT_EXP);
+ // Expiration time
+ double nbf = cupsJWTGetClaimNumber(jwt, CUPS_JWT_NBF);
+ // Not before time
+ char date[256]; // Date
+
+ if (iss)
+ printf("Issuer: %s\n", iss);
+ if (name)
+ printf("Display Name: %s\n", name);
+ if (sub)
+ printf("Subject: %s\n", sub);
+ if (aud)
+ printf("Audience: %s\n", aud);
+ if (jti)
+ printf("JWT ID: %s\n", jti);
+ if (iat > 0.0)
+ printf("Issued On: %s\n", httpGetDateString2((time_t)iat, date, sizeof(date)));
+ if (exp > 0.0)
+ printf("Expires On: %s\n", httpGetDateString2((time_t)exp, date, sizeof(date)));
+ if (nbf > 0.0)
+ printf("Not Before: %s\n", httpGetDateString2((time_t)nbf, date, sizeof(date)));
+ printf("Valid: %s\n", jwks ? (cupsJWTHasValidSignature(jwt, jwks) ? "yes" : "no") : "unknown");
+
+ if ((temp = cupsJSONExportString(headers)) != NULL)
+ {
+ printf("\njose=%s\n", temp);
+ free(temp);
+ }
+
+ if ((temp = cupsJSONExportString(claims)) != NULL)
+ {
+ printf("\nclaims=%s\n", temp);
+ free(temp);
+ }
cupsJWTDelete(jwt);
}