Skip to content

Patch keygen#2

Merged
istepic merged 6 commits intorel0.4.12from
patch-keygen
Dec 5, 2022
Merged

Patch keygen#2
istepic merged 6 commits intorel0.4.12from
patch-keygen

Conversation

@istepic
Copy link
Owner

@istepic istepic commented Nov 17, 2022

No description provided.

Copy link

@garw garw left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I commented on some of the locations. Generally, I feel that the error handling needs a cleanup. While these different error codes depending on where the code bails out may be helpful for debugging, it is not really what one usually does. In particular, when it gets to the (real) library user, error values need to have a customer-understandable meaning without reading the code.

src/p11_key.c Outdated

if (pkcs11_get_session(slot, 1, &session))
return -1;
return -10;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why? What are the magical numbers? At least add a comment.

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As you said, debugging, but I'm fine with -1 too.

src/p11_key.c Outdated
int curve_nid = NID_undef;

if (pkcs11_get_session(slot, 1, &session)) {
return -20;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is it here -20 and above -10?

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

s.a.

ecdsa_params = (unsigned char *)OPENSSL_malloc(ecdsa_params_len);
if (!ecdsa_params)
return -23;
tmp = ecdsa_params;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what is the purpose of tmp?

Copy link
Owner Author

@istepic istepic Nov 24, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some leftover. Done
Turns out this breaks a test. It's somehow changing ecdsa_params_len indirectly by changing curve_obj?

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Doesn't make sense imho.
Both tmp and ecdsa_params are of type "unsigned char *".
Just for clarification: Did you also changed from tmp to ecdsa_params in the next line?

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the code I took from the other unmerged EC keygen PR. I will take a deeper look in openssl functions to see what is this magic.

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So, in line 350, what actually happens is memory space pointed by ecdsa_params gets filled in some way but this also changes ecdsa_params pointer (as a side effect?) which we for some reason don't want and that's why we use a tmp pointer. I don't really understand this cryptic code in openssl:

  void ASN1_put_object(unsigned char **pp, int constructed, int length, int tag,
                       int xclass)
  {
      unsigned char *p = *pp;
      int i, ttag;
  
      i = (constructed) ? V_ASN1_CONSTRUCTED : 0;
      i |= (xclass & V_ASN1_PRIVATE);
      if (tag < 31)
          *(p++) = i | (tag & V_ASN1_PRIMITIVE_TAG);
      else {
          *(p++) = i | V_ASN1_PRIMITIVE_TAG;
          for (i = 0, ttag = tag; ttag > 0; i++)
              ttag >>= 7;
          ttag = i;
          while (i-- > 0) {
              p[i] = tag & 0x7f;
              if (i != (ttag - 1))
                  p[i] |= 0x80;
              tag >>= 7;
          }
          p += ttag;                                                         
      }
      if (constructed == 2)
          *(p++) = 0x80;
      else
          asn1_put_length(&p, length);
      *pp = p;
  }

and I think spending time to understand what this is doing is not worth it given our current situation. Especially since this seems to work as evidenced by the test, and this behavior only depends on curve_obj which is quite straightforward. So I don't anticipate things going wrong wrong here.

src/p11_key.c Outdated
if (label)
pkcs11_addattr_s(&pubtmpl, CKA_LABEL, label);
pkcs11_addattr_bool(&pubtmpl, CKA_TOKEN, TRUE);
pkcs11_addattr_bool(&pubtmpl, CKA_DERIVE, TRUE);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we really have DERIVE on the public key?

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done. Maybe we should revisit these options to see if they match our use cases. Or make them configurable?

src/p11_front.c Outdated
size_t key_id_len = 0;
if (kg && kg->key_id) {
key_id_len = strlen(kg->key_id);
if (key_id_len > 127) {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While this is not a security problem, should hex to bin compress the output by factor 1/2?

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you mean output length? For even lengths yes, for odd no.
0x10a (3 bytes) -> 0000 0001 0000 1010 (2 bytes)

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if (key_id_len > 127) {
if (key_id_len == 127) {

RETURN VALUE
The strnlen() function returns strlen(s), if that is less than maxlen, or maxlen if there is no null termi‐
nating ('\0') among the first maxlen characters pointed to by s.

I wonder why something greater than 127 shall be returned in any case.


typedef struct PKCS11_ec_kgen_st {
const char *curve;
} PKCS11_EC_KGEN;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any particular reason we safe two bytes of memory here and not just write KEYGEN?

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like shorter type names if it's trivial to deduce what it is. I can switch if you insist.

src/eng_back.c Outdated
}

// Take the first token that has a matching label
// TODO: make this more intelligent
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Todo? Do we want to change this? I guess its okay for now as this is an intermediate solution anyways.

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah. Done

tests/keygen.c Outdated
fprintf(stderr, "Where are the keys?\n");
return -1;
}
if(!(mdctx = EVP_MD_CTX_create())) {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

style: We dislike assignments in if () statements.

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

exit(1);
}

ENGINE_finish(engine);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you also add test cases that extract the respective keys and use those to verify the signature?

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is what the test does. After successful ENGINE_ctrl_cmd(engine, "KEYGEN", 0, &eckg, NULL, 1); the keypair is living on the HSM. Then we extract it with

EVP_PKEY *ecpb = ENGINE_load_public_key(engine, "1234", NULL, NULL);
EVP_PKEY *ecpr = ENGINE_load_private_key(engine, "1234", NULL, NULL);

and use it to sign and verify.

Copy link

@wusto wusto left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

found some minor things.

Otherwise it looks good to me.

src/p11_front.c Outdated
return pkcs11_generate_key(slot, algorithm, bits, label, id, id_len);
unsigned char out[128] = {0};
size_t key_id_len = 0;
if (kg && kg->key_id) {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if (kg && kg->key_id) {
if (!kg) {
return -1;
}
if (kg->key_id) {

otherwise kg->type will cause an SIGSEGV in the end.

src/p11_front.c Outdated
size_t key_id_len = 0;
if (kg && kg->key_id) {
key_id_len = strlen(kg->key_id);
if (key_id_len > 127) {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if (key_id_len > 127) {
if (key_id_len == 127) {

RETURN VALUE
The strnlen() function returns strlen(s), if that is less than maxlen, or maxlen if there is no null termi‐
nating ('\0') among the first maxlen characters pointed to by s.

I wonder why something greater than 127 shall be returned in any case.

case EVP_PKEY_EC:
return pkcs11_ec_keygen(slot, kg->kgen.ec->curve,
kg->key_label, out, key_id_len);
default:
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add pkcs11_dilithium_keygen here ;)

ecdsa_params = (unsigned char *)OPENSSL_malloc(ecdsa_params_len);
if (!ecdsa_params)
return -23;
tmp = ecdsa_params;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Doesn't make sense imho.
Both tmp and ecdsa_params are of type "unsigned char *".
Just for clarification: Did you also changed from tmp to ecdsa_params in the next line?

display_openssl_errors(__LINE__);
fprintf(stderr, "Verify fail\n");
retval = -10;
goto err;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

superfluous.

or also add it to the if-branch (not just the else) if you want to avoid stupid failures on extending the test. :)

As discussed in OpenSC#379 and
OpenSC#378 we need a generic interface
that supports multiple algorithms for key generation. Attempt was made
to create a new keygen method and register it in PKCS11_pkey_meths() in
p11_pkey.c (so that it's possible to generate keys using OpenSSL's
EVP_PKEY_* API) but multiple design issues appeared. How and where do you
pass the key ID, token label and alike was the first question. As
suggested by the maintainer here:
OpenSC#379 (comment),
app_data from EVP_PKEY_CTX was (mis)used and that worked well. The
reason why this approach was abandoned is because a good (or bad) way
to get a handle of the PKCS11_CTX_private, that is necessary for the
Cryptoki call, was not found.
The way other operations work is that they rely on the key being
loaded *_first_* through ENGINE_load_public(private)_key because this
is when the PKCS11_CTX gets initialized and a handle to
PKCS11_OBJECT_private gets set to the ex_data of the underlying key.
Key generation obviously cannot rely on that mechanism since key
doesn't yet exist.

Instead, a generic PKCS11_generate_key interface was made that
takes a structure describing the key generation algorithm. For now
it only contains simple options like curve name for ECC or number
of bits for RSA key generation. This interface can then be used
as any other PKCS11 wrapper interface or using the ENGINE control
commands. Using it with ENGINE control commands is demonstrated in
the new tests/keygen.c file.

Code for ECC keygen was taken from:
OpenSC#379 and reworked to compile and
work with some new additions to libp11 i.e. templates.
for identifying operations and libp11 already uses these identifiers,
we keep using these for consistency.
- added NULL ptr checks
- fixed the usage of strnlen when handling key_id
- added another goto in keygen test
@istepic istepic merged commit fb885ab into rel0.4.12 Dec 5, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants