Skip to content
This repository has been archived by the owner on Nov 11, 2024. It is now read-only.

Pubkey not set but signature verification possible #26

Open
DedieuPY opened this issue Mar 28, 2023 · 8 comments
Open

Pubkey not set but signature verification possible #26

DedieuPY opened this issue Mar 28, 2023 · 8 comments
Labels
enhancement New feature or request

Comments

@DedieuPY
Copy link

Hello,
I have a problem (or rather a strange behavior) with the lib rhonabwy.

First, here is my code :

const char token[] = "eyJhbGciOiJFUzUxMiIsInR5cCI6IkpXVCJ9."                // header
                     "eyJleHBpcmF0aW9uRGF0ZSI6MTIzNDU2Nzg5MCwibXNnQ2hrQSI6MTIsIm1zZ0Noa0IiOjM0LCJ0b2tlblR5cGUiOiJTIn0."   // payload
                     "AOyZC-Jf2rt6BpSjzNHk0hyLBS96DpBWDol58gORVjTMpJNoU_ICE6ePLCBq24kW4CgN_XDV3cjtSl8CjG4HZ3zAAAAOForo1kNG5PjBzItBsf9AS5fq_E8DBi99o8xZvZBTqTunYLNXE048WG5r88AfnNLCnYCioiSIg47774r760Eu"; // signature

const unsigned char pubKey[] = "-----BEGIN PUBLIC KEY-----\n"
            "MIGbMBAGByqGSM49AgEGBSuBBAAjA4GGAAQBgc4HZz+/fBbC7lmEww0AO3NK9wVZ\n"
            "PDZ0VEnsaUFLEYpTzb90nITtJUcPUbvOsdZIZ1Q8fnbquAYgxXL5UgHMoywAib47\n"
            "6MkyyYgPk0BXZq3mq4zImTRNuaU9slj9TVJ3ScT3L1bXwVuPJDzpr5GOFpaj+WwM\n"
            "Al8G7CqwoJOsW7Kddns=\n"
            "-----END PUBLIC KEY-----\n";

const unsigned char privKey[] = "-----BEGIN PRIVATE KEY-----\n"
            "MIHuAgEAMBAGByqGSM49AgEGBSuBBAAjBIHWMIHTAgEBBEIBiyAa7aRHFDCh2qga\n"
            "9sTUGINE5jHAFnmM8xWeT/uni5I4tNqhV5Xx0pDrmCV9mbroFtfEa0XVfKuMAxxf\n"
            "Z6LM/yKhgYkDgYYABAGBzgdnP798FsLuWYTDDQA7c0r3BVk8NnRUSexpQUsRilPN\n"
            "v3SchO0lRw9Ru86x1khnVDx+duq4BiDFcvlSAcyjLACJvjvoyTLJiA+TQFdmrear\n"
            "jMiZNE25pT2yWP1NUndJxPcvVtfBW48kPOmvkY4WlqP5bAwCXwbsKrCgk6xbsp12\n"
            "ew==\n"
            "-----END PRIVATE KEY-----\n";

jwt_t * jwt;

r_global_init();
r_jwt_init(&jwt);

r_jwt_add_sign_keys_pem_der(jwt, R_FORMAT_PEM, privKey, sizeof(privKey), pubKey, sizeof(pubKey));
r_jwt_advanced_parse(jwt, token,R_PARSE_NONE, R_FLAG_IGNORE_SERVER_CERTIFICATE);
r_jwt_set_sign_alg(jwt, R_JWA_ALG_ES512);
printf("PRIV : %s\n", r_jwks_export_to_json_str(jwt->jwks_privkey_sign, 0));
printf("PUB : %s\n", r_jwks_export_to_json_str(jwt->jwks_pubkey_sign, 0));

r_jwt_parse(jwt, token, 0);
r_jwt_verify_signature(jwt, NULL, 0);

printf("CLAIM = %s\n", r_jwt_get_full_claims_str(jwt));

And here is an execution result :

PRIV : {"keys":[{"kty":"EC","x":"AYHOB2c_v3wWwu5ZhMMNADtzSvcFWTw2dFRJ7GlBSxGKU82_dJyE7SVHD1G7zrHWSGdUPH526rgGIMVy-VIBzKMs","y":"ib476MkyyYgPk0BXZq3mq4zImTRNuaU9slj9TVJ3ScT3L1bXwVuPJDzpr5GOFpaj-WwMAl8G7CqwoJOsW7Kddns","d":"AYsgGu2kRxQwodqoGvbE1BiDROYxwBZ5jPMVnk_7p4uSOLTaoVeV8dKQ65glfZm66BbXxGtF1XyrjAMcX2eizP8i","crv":"P-521","kid":"CatmrLCuBa_kI3VMfembQnugtVauN35XuHIRRGZuXzY"}]}
PUB : {"keys":[]}
CLAIM = {"expirationDate":1234567890,"msgChkA":12,"msgChkB":34,"tokenType":"S"}

As you can see, apparently my public key was not set correctly but my token signature was verifiy.

Do you have an explanation please?

Thanks in advance

@babelouest
Copy link
Owner

Hello,

I can't reproduce your issue as-is, my output is:

PRIV : {"keys":[{"kty":"EC","x":"AYHOB2c_v3wWwu5ZhMMNADtzSvcFWTw2dFRJ7GlBSxGKU82_dJyE7SVHD1G7zrHWSGdUPH526rgGIMVy-VIBzKMs","y":"ib476MkyyYgPk0BXZq3mq4zImTRNuaU9slj9TVJ3ScT3L1bXwVuPJDzpr5GOFpaj-WwMAl8G7CqwoJOsW7Kddns","d":"AYsgGu2kRxQwodqoGvbE1BiDROYxwBZ5jPMVnk_7p4uSOLTaoVeV8dKQ65glfZm66BbXxGtF1XyrjAMcX2eizP8i","crv":"P-521","kid":"CatmrLCuBa_kI3VMfembQnugtVauN35XuHIRRGZuXzY"}]}
PUB : {"keys":[{"kty":"EC","x":"AYHOB2c_v3wWwu5ZhMMNADtzSvcFWTw2dFRJ7GlBSxGKU82_dJyE7SVHD1G7zrHWSGdUPH526rgGIMVy-VIBzKMs","y":"ib476MkyyYgPk0BXZq3mq4zImTRNuaU9slj9TVJ3ScT3L1bXwVuPJDzpr5GOFpaj-WwMAl8G7CqwoJOsW7Kddns","crv":"P-521","kid":"CatmrLCuBa_kI3VMfembQnugtVauN35XuHIRRGZuXzY"}]}
CLAIM = {"expirationDate":1234567890,"msgChkA":12,"msgChkB":34,"tokenType":"S"}

Although, if you add a private key to a JWT private keyset, its intention is to generate a new signed token. If you just want to verify a signature, you can only add the public key into the jwt's keyset, or even better, specify the public key when calling r_jwt_verify_signature.

@DedieuPY
Copy link
Author

DedieuPY commented Mar 28, 2023

Hi @babelouest ,

First of all, thank you for your quick response. I have changed my code this way :

                     "eyJleHBpcmF0aW9uRGF0ZSI6MTIzNDU2Nzg5MCwibXNnQ2hrQSI6MTIsIm1zZ0Noa0IiOjM0LCJ0b2tlblR5cGUiOiJTIn0."   // payload
                     "AOyZC-Jf2rt6BpSjzNHk0hyLBS96DpBWDol58gORVjTMpJNoU_ICE6ePLCBq24kW4CgN_XDV3cjtSl8CjG4HZ3zAAAAOForo1kNG5PjBzItBsf9AS5fq_E8DBi99o8xZvZBTqTunYLNXE048WG5r88AfnNLCnYCioiSIg47774r760Eu"; // signature

const unsigned char pubKey[] = "-----BEGIN PUBLIC KEY-----\n"
            "MIGbMBAGByqGSM49AgEGBSuBBAAjA4GGAAQBgc4HZz+/fBbC7lmEww0AO3NK9wVZ\n"
            "PDZ0VEnsaUFLEYpTzb90nITtJUcPUbvOsdZIZ1Q8fnbquAYgxXL5UgHMoywAib47\n"
            "6MkyyYgPk0BXZq3mq4zImTRNuaU9slj9TVJ3ScT3L1bXwVuPJDzpr5GOFpaj+WwM\n"
            "Al8G7CqwoJOsW7Kddns=\n"
            "-----END PUBLIC KEY-----\n";

const unsigned char privKey[] = "-----BEGIN PRIVATE KEY-----\n"
            "MIHuAgEAMBAGByqGSM49AgEGBSuBBAAjBIHWMIHTAgEBBEIBiyAa7aRHFDCh2qga\n"
            "9sTUGINE5jHAFnmM8xWeT/uni5I4tNqhV5Xx0pDrmCV9mbroFtfEa0XVfKuMAxxf\n"
            "Z6LM/yKhgYkDgYYABAGBzgdnP798FsLuWYTDDQA7c0r3BVk8NnRUSexpQUsRilPN\n"
            "v3SchO0lRw9Ru86x1khnVDx+duq4BiDFcvlSAcyjLACJvjvoyTLJiA+TQFdmrear\n"
            "jMiZNE25pT2yWP1NUndJxPcvVtfBW48kPOmvkY4WlqP5bAwCXwbsKrCgk6xbsp12\n"
            "ew==\n"
            "-----END PRIVATE KEY-----\n";

 r_global_init();

/* jwt_t * jwt;
r_jwt_init(&jwt); */
int iterations = 5000;
double sum = 0;
clock_t start, stop;

jwt_t * jwt;
jwk_t * jwk;
r_jwt_init(&jwt);    
r_jwk_init(&jwk);


printf("=================== IS VERIFY WORKING ? ===================\n");
printf("ADD PEM : %d\n", r_jwt_add_sign_keys_pem_der(jwt, R_FORMAT_PEM, privKey, sizeof(privKey), pubKey, sizeof(pubKey)));
r_jwt_set_sign_alg(jwt, R_JWA_ALG_ES512);
//r_jwt_set_sign_alg(jwt, R_JWA_ALG_ES256);

printf("PRIV : %s\n", r_jwks_export_to_json_str(jwt->jwks_privkey_sign, 0));
printf("PUB : %s\n", r_jwks_export_to_json_str(jwt->jwks_pubkey_sign, 0));

printf("JWK ? %d\n", r_jwk_append_x5c(jwk, R_FORMAT_PEM, pubKey, sizeof(pubKey)));

r_jwt_parse(jwt, token, 0);
r_jwt_verify_signature(jwt, jwk, 0);

and the output :

ADD PEM : 1
PRIV : {"keys":[{"kty":"EC","x":"AYHOB2c_v3wWwu5ZhMMNADtzSvcFWTw2dFRJ7GlBSxGKU82_dJyE7SVHD1G7zrHWSGdUPH526rgGIMVy-VIBzKMs","y":"ib476MkyyYgPk0BXZq3mq4zImTRNuaU9slj9TVJ3ScT3L1bXwVuPJDzpr5GOFpaj-WwMAl8G7CqwoJOsW7Kddns","d":"AYsgGu2kRxQwodqoGvbE1BiDROYxwBZ5jPMVnk_7p4uSOLTaoVeV8dKQ65glfZm66BbXxGtF1XyrjAMcX2eizP8i","crv":"P-521","kid":"CatmrLCuBa_kI3VMfembQnugtVauN35XuHIRRGZuXzY"}]}
PUB : {"keys":[]}
JWK ? 3
CLAIM = {"expirationDate":1234567890,"msgChkA":12,"msgChkB":34,"tokenType":"S"}

So I have apparently an error 3 (so RHN_ERROR_PARAM) but I don't really see why.

EDIT :

I added the log display (thanks yder) and I have this:

2023-03-28T12:29:11Z - Yder Tests ERROR: r_jwk_import_from_gnutls_pubkey ecdsa - Error curve
2023-03-28T12:29:11Z - Yder Tests ERROR: r_jwt_add_sign_keys_pem_der - Error parsing pubkey
ADD PEM : 1
PRIV : {"keys":[{"kty":"EC","x":"EVs_o5-uQbTjL3chynL4wXgUg2R9q9UU8I5mEovUf84","y":"kGe5DgSIycKp8w9aJmoHhB1sB3QTugfnRWm5nU_TzsY","d":"evZzL1gdAFr88hb2OF_2NxApJCzGCEDdfSp6VQO30hw","crv":"P-256","kid":"pA8PLil03HaoLfb_4I7Q15lVx5560MINl66Pkkat4cE"}]}
PUB : {"keys":[]}
2023-03-28T12:29:11Z - Yder Tests ERROR: r_jwk_append_x5c - Error gnutls_x509_crt_import: Base64 unexpected header error.
JWK ? 3
2023-03-28T12:29:11Z - Yder Tests ERROR: r_jwk_is_valid - Missing kty
2023-03-28T12:29:11Z - Yder Tests ERROR: r_jwk_is_valid - Invalid kty
CLAIM = {"expirationDate":1234567890,"msgChkA":789456123,"msgChkB":147852369,"tokenType":"S"}

@babelouest
Copy link
Owner

You're using r_jwk_append_x5c to import a public key, this function expects a x5 certificate, tha's why I also have the RHN_ERROR_PARAM.

Nevertheless, you should test all return values to make sure RHN_OK is always returned, otherwise you should stop the process.

You also shouldn't call r_jwt_set_sign_alg, the alg is set during the token parse. But you can verify that the alg set is the expected one: if (r_jwt_get_sign_alg(jwt) == R_JWA_ALG_ES512).

Are you using the last rhonabwy release? If not, you also should, that would explain our differences.

I refactored your code to make it more readable and so it won't continue after an error. Note that calling r_jwt_add_sign_keys_pem_der and r_jwt_set_sign_alg are useless and could lead to error in this case, so you should skip them.

#include <stdio.h>
#include <yder.h>
#include <rhonabwy.h>

int main() {
  const char token[] = "eyJhbGciOiJFUzUxMiIsInR5cCI6IkpXVCJ9."                // header
                       "eyJleHBpcmF0aW9uRGF0ZSI6MTIzNDU2Nzg5MCwibXNnQ2hrQSI6MTIsIm1zZ0Noa0IiOjM0LCJ0b2tlblR5cGUiOiJTIn0."   // payload
                       "AOyZC-Jf2rt6BpSjzNHk0hyLBS96DpBWDol58gORVjTMpJNoU_ICE6ePLCBq24kW4CgN_XDV3cjtSl8CjG4HZ3zAAAAOForo1kNG5PjBzItBsf9AS5fq_E8DBi99o8xZvZBTqTunYLNXE048WG5r88AfnNLCnYCioiSIg47774r760Eu"; // signature

  const unsigned char pubKey[] = "-----BEGIN PUBLIC KEY-----\n"
              "MIGbMBAGByqGSM49AgEGBSuBBAAjA4GGAAQBgc4HZz+/fBbC7lmEww0AO3NK9wVZ\n"
              "PDZ0VEnsaUFLEYpTzb90nITtJUcPUbvOsdZIZ1Q8fnbquAYgxXL5UgHMoywAib47\n"
              "6MkyyYgPk0BXZq3mq4zImTRNuaU9slj9TVJ3ScT3L1bXwVuPJDzpr5GOFpaj+WwM\n"
              "Al8G7CqwoJOsW7Kddns=\n"
              "-----END PUBLIC KEY-----\n";

  const unsigned char privKey[] = "-----BEGIN PRIVATE KEY-----\n"
              "MIHuAgEAMBAGByqGSM49AgEGBSuBBAAjBIHWMIHTAgEBBEIBiyAa7aRHFDCh2qga\n"
              "9sTUGINE5jHAFnmM8xWeT/uni5I4tNqhV5Xx0pDrmCV9mbroFtfEa0XVfKuMAxxf\n"
              "Z6LM/yKhgYkDgYYABAGBzgdnP798FsLuWYTDDQA7c0r3BVk8NnRUSexpQUsRilPN\n"
              "v3SchO0lRw9Ru86x1khnVDx+duq4BiDFcvlSAcyjLACJvjvoyTLJiA+TQFdmrear\n"
              "jMiZNE25pT2yWP1NUndJxPcvVtfBW48kPOmvkY4WlqP5bAwCXwbsKrCgk6xbsp12\n"
              "ew==\n"
              "-----END PRIVATE KEY-----\n";

  r_global_init();
  y_init_logs("issue 26", Y_LOG_MODE_CONSOLE, Y_LOG_LEVEL_DEBUG, NULL, "Starting issue 26 tester");

  jwt_t * jwt = NULL;
  jwk_t * jwk = NULL;
  int ret;

  do {
    if ((ret = r_jwt_init(&jwt)) != RHN_OK) {
      printf("r_jwt_init error: %d\n", ret);
      break;
    }
    
    if ((ret = r_jwk_init(&jwk)) != RHN_OK) {
      printf("r_jwk_init error: %d\n", ret);
      break;
    }
    
    if ((ret = r_jwt_add_sign_keys_pem_der(jwt, R_FORMAT_PEM, privKey, sizeof(privKey), pubKey, sizeof(pubKey))) != RHN_OK) {
      printf("r_jwt_add_sign_keys_pem_der error: %d\n", ret);
      break;
    }
    
    if ((ret = r_jwt_set_sign_alg(jwt, R_JWA_ALG_ES512)) != RHN_OK) {
      printf("r_jwt_set_sign_alg error: %d\n", ret);
      break;
    }
    
    printf("PRIV : %s\n", r_jwks_export_to_json_str(jwt->jwks_privkey_sign, 0));
    printf("PUB : %s\n", r_jwks_export_to_json_str(jwt->jwks_pubkey_sign, 0));
    
    if ((ret = r_jwk_import_from_pem_der(jwk, R_X509_TYPE_PUBKEY, R_FORMAT_PEM, pubKey, sizeof(pubKey))) != RHN_OK) {
      printf("r_jwk_append_x5c error: %d\n", ret);
      break;
    }
    
    if ((ret = r_jwt_parse(jwt, token, 0)) != RHN_OK) {
      printf("r_jwk_init error: %d\n", ret);
      break;
    }
    
    if ((ret = r_jwt_verify_signature(jwt, jwk, 0)) != RHN_OK) {
      printf("r_jwt_verify_signature error: %d\n", ret);
      break;
    } else {
      printf("r_jwt_verify_signature ok\n");
    }
  } while (0);
  r_jwk_free(jwk);
  r_jwt_free(jwt);
  y_close_logs();
}

you can compile it with the following command: gcc -o test test.c -lrhonabwy -lyder.

With this code, I have the following output:

2021-07-09T02:11:00Z - issue 26 INFO: Starting issue 26 tester
PRIV : {"keys":[{"kty":"EC","x":"AYHOB2c_v3wWwu5ZhMMNADtzSvcFWTw2dFRJ7GlBSxGKU82_dJyE7SVHD1G7zrHWSGdUPH526rgGIMVy-VIBzKMs","y":"ib476MkyyYgPk0BXZq3mq4zImTRNuaU9slj9TVJ3ScT3L1bXwVuPJDzpr5GOFpaj-WwMAl8G7CqwoJOsW7Kddns","d":"AYsgGu2kRxQwodqoGvbE1BiDROYxwBZ5jPMVnk_7p4uSOLTaoVeV8dKQ65glfZm66BbXxGtF1XyrjAMcX2eizP8i","crv":"P-521","kid":"CatmrLCuBa_kI3VMfembQnugtVauN35XuHIRRGZuXzY"}]}
PUB : {"keys":[{"kty":"EC","x":"AYHOB2c_v3wWwu5ZhMMNADtzSvcFWTw2dFRJ7GlBSxGKU82_dJyE7SVHD1G7zrHWSGdUPH526rgGIMVy-VIBzKMs","y":"ib476MkyyYgPk0BXZq3mq4zImTRNuaU9slj9TVJ3ScT3L1bXwVuPJDzpr5GOFpaj-WwMAl8G7CqwoJOsW7Kddns","crv":"P-521","kid":"CatmrLCuBa_kI3VMfembQnugtVauN35XuHIRRGZuXzY"}]}
r_jwt_verify_signature ok

@DedieuPY
Copy link
Author

With your code, I have the following output :

2023-03-28T12:44:25Z - issue 26 INFO: Starting issue 26 tester
2023-03-28T12:44:25Z - issue 26 ERROR: r_jwk_import_from_gnutls_pubkey ecdsa - Error curve
2023-03-28T12:44:25Z - issue 26 ERROR: r_jwt_add_sign_keys_pem_der - Error parsing pubkey
r_jwt_add_sign_keys_pem_der error: 1

Are you using the last rhonabwy release? If not, you also should, that would explain our differences.

I took the code of the branch master

@babelouest
Copy link
Owner

What system/version are you using?
What version of GnuTLS is installed?

@DedieuPY
Copy link
Author

I'm using Yocto 2.5, with GnuTLS 3.6.1 (which is supposed to be the minimum version required for ECDSA if I understand correctly)

However, i tried this code but with the RS512 algo and it works correctly. So it seems to be a sepcific problem with ECDSA (or at least depending on the algo used)

@babelouest
Copy link
Owner

I'm using Yocto 2.5, with GnuTLS 3.6.1 (which is supposed to be the minimum version required for ECDSA if I understand correctly)

Maybe that's the problem with rhonabwy, I assumed ECDSA was supported since GnuTLS 3.6 but it's probably from a later version.

@DedieuPY
Copy link
Author

Perhaps
If I can find out which version of GnuTLS ECDSA works, I'll tell you.

But thanks for taking the time to help me!

@babelouest babelouest added the enhancement New feature or request label Mar 29, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

2 participants