Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

sc-hsm-tool: Add options for public key authentication #1711

Closed
wants to merge 23 commits into from

Conversation

frankbraun
Copy link

This PR extends the sc-hsm-tool with options to use Public Key Authentication (n-of-m Authentication).

The new options allow the sc-hsm-tool to:

  • Initialize a HSM for public key authentication (--public-key-auth and --required-pub-keys).
  • To export a public key to be used for public key authentication (--export-for-pub-key-auth).
  • To register such an exported public key with an HSM initialized for public key authentication (--register--public-key).
  • Show the status of public key authentication (public-key-auth-status).

What's still missing is an option to do the actual authentication.

The SmartCard-HSM version 2.6 and SmartCard-HSM version 3.1 were used during testing.

Open issue

The CAR and CHR of CV Certificates generated with OpenSC are fixed values, see:

strlcpy(cvc.car, "UTCA00001", sizeof cvc.car);
strlcpy(cvc.chr, "UTTM00001", sizeof cvc.chr);

This prevents registering multiple keys (created with OpenSC) for public key authentication due to name clashes.

This issue has been discussed with Andreas Schwier from @CardContact, but no solution has been found yet.

Checklist
  • Documentation is added or updated

@frankmorgner
Copy link
Member

CAR/CHR are tied to a limit of 7-16 bytes by BSI TR-03110.

@CardContact
Copy link
Member

Ideally, one could specify the CHR and inner CAR used in the certificate signing request signed by the SmartCard-HSM when generating a new key pair.

In our own PKCS#11 module we do this with proprietary CKA_ attributes that carry information like CHR, CAR, algorithm list, key use counter etc. Unfortunately OpenSC and the pkcs11-tool does not have the capability to specify custom attributes in the keygen template.

In Public Key Authentication (PKA), the public key is imported and validated in the CSR format and the CHR is used as unique key identifier. With a static CHR this doesn't work with more that one key.

Patch #1714 replaces the static CHR with an id based on the device serial number (which in fact is the CHR of the device certificate without serial 00000). As a result, the CSR would contain a CHR that is unique per card, which should be sufficient for PKA.

Copy link
Member

@frankmorgner frankmorgner left a comment

Choose a reason for hiding this comment

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

Please be more efficient with certificate parsing, i.e. use OpenPACE or sc_pkcs15emu_sc_hsm_decode_cvc().

Your code contains a lot of cleanup overhead. We often use a goto statement to tie cleanup of ressources to the end of a function. Your code would benifit from that pattern as well.

src/libopensc/card-sc-hsm.c Outdated Show resolved Hide resolved
src/libopensc/card-sc-hsm.c Outdated Show resolved Hide resolved
/* return CAR */
strncpy((char*) carstr, (const char*) car, taglen);
LOG_FUNC_RETURN(ctx, SC_SUCCESS);
}
Copy link
Member

Choose a reason for hiding this comment

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

Maybe you also want to look at OpenPACE for more extended parsing/verification of CVCs. If you want to keep this self sustained within OpenSC, you should use sc_pkcs15emu_sc_hsm_decode_cvc()

Copy link
Author

Choose a reason for hiding this comment

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

I tried to parse this with sc_pkcs15emu_sc_hsm_decode_cvc(), but wasn't able to. I got the following error:

Required ASN.1 object not found

Copy link
Member

Choose a reason for hiding this comment

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

The idea is not to parse ASN.1 by hand. Please check what object is missing and why.

src/libopensc/card-sc-hsm.c Outdated Show resolved Hide resolved
src/libopensc/card-sc-hsm.c Outdated Show resolved Hide resolved
src/libopensc/card-sc-hsm.c Outdated Show resolved Hide resolved
src/tools/sc-hsm-tool.c Outdated Show resolved Hide resolved
src/tools/sc-hsm-tool.c Show resolved Hide resolved
src/tools/sc-hsm-tool.c Show resolved Hide resolved
src/tools/sc-hsm-tool.c Show resolved Hide resolved
@frankbraun
Copy link
Author

Thanks for the feedback @frankmorgner, I'll address the remarks.

src/libopensc/card-sc-hsm.c Outdated Show resolved Hide resolved
src/libopensc/card-sc-hsm.c Outdated Show resolved Hide resolved
src/libopensc/card-sc-hsm.c Outdated Show resolved Hide resolved
@frankbraun
Copy link
Author

I tried to address all remarks. Is there anything else that needs to be changed? Should I squash commits to cleanup the commit history?

Copy link
Member

@frankmorgner frankmorgner left a comment

Choose a reason for hiding this comment

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

The code looks OK, but you really need to use sc_pkcs15emu_sc_hsm_decode_cvc()!

src/libopensc/card-sc-hsm.c Show resolved Hide resolved
}

/* C_DevAut */
fid[0] = 0x2F;
Copy link
Member

Choose a reason for hiding this comment

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

That's EF_C_DevAut, see

if (priv->EF_C_DevAut && priv->EF_C_DevAut_len) {
all_certs_len = priv->EF_C_DevAut_len;
cert = priv->EF_C_DevAut;
} else {
/* get issuer and device certificate from the card */
r = sc_path_set(&path, SC_PATH_TYPE_FILE_ID, (u8 *) "\x2F\x02", 2, 0, 0);
if (r < 0)
goto err;
r = sc_select_file(card, &path, NULL);
if (r < 0)
goto err;
r = sc_read_binary(card, 0, all_certs, all_certs_len, 0);
if (r < 0)
goto err;
if (r == 0) {
r = SC_ERROR_FILE_NOT_FOUND;
goto err;
}
all_certs_len = r;
/* save EF_C_DevAut for further use */
cert = realloc(priv->EF_C_DevAut, all_certs_len);
if (cert) {
memcpy((unsigned char *) cert, all_certs, all_certs_len);
priv->EF_C_DevAut = (unsigned char *) cert;
priv->EF_C_DevAut_len = all_certs_len;
}
cert = all_certs;
}
left = all_certs_len;
device_cert = cert;
r = sc_pkcs15emu_sc_hsm_decode_cvc(&p15card, &cert, &left, &cvc_device);
if (r < 0)
goto err;
device_cert_len = all_certs_len - left;
issuer_cert = cert;
r = sc_pkcs15emu_sc_hsm_decode_cvc(&p15card, &cert, &left, &cvc_issuer);
if (r < 0)
goto err;
issuer_cert_len = all_certs_len - device_cert_len - left;

return r;
}

static void get_CHR(char *chrstr, int is_cvc, sc_context_t *ctx, const u8 *buf, size_t buflen)
Copy link
Member

Choose a reason for hiding this comment

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

please use sc_pkcs15emu_sc_hsm_decode_cvc for parsing

Copy link
Author

Choose a reason for hiding this comment

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

Regarding EF_C_DevAut I can see that it is set in the static function sc_hsm_perform_chip_authentication(). sc_hsm_perform_chip_authentication() is only called from sc_hsm_pin_cmd(). That is, filling EF_C_DevAut currently requires PIN entry, which is not necessary for my implementation.

What would be a good way to use EF_C_DevAut, ideally without PIN entry?

Copy link
Author

Choose a reason for hiding this comment

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

Regarding sc_pkcs15emu_sc_hsm_decode_cvc(): I spent many hours trying to get it to work.
Like I wrote in #1711 (comment), I wasn't able to parse the device certificate and the device issuer certificate with it. For the public key, sc_cvc_t.car is filled correctly, but sc_cvc_t.outer_car contains garbage. Unfortunately, I couldn't figure out what the problem is.

Copy link
Member

Choose a reason for hiding this comment

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

What would be a good way to use EF_C_DevAut, ideally without PIN entry?

The basic idea is in sc-hsm-tool to

  1. force a sc-hsm card driver, as done here and
  2. then cast card->drv_data to struct sc_hsm_private_data * and use its EF_C_DevAut if it's available.

Additionally, you may want to transfer ownership of a newly read EF_C_DevAut to the driver for re-use. You don't need to perform CA to do this.

src/tools/sc-hsm-tool.c Show resolved Hide resolved
@frankbraun
Copy link
Author

The code looks OK, but you really need to use sc_pkcs15emu_sc_hsm_decode_cvc()!

I tried to refactor the code in order to use sc_pkcs15emu_sc_hsm_decode_cvc().
I can successfully use it to parse the public key CVC, which has the following structure:

class=1, tag=7, compound=true, len=493, fullLen=497, sha1=9874645e2b9257125c1c7f8d58cc2fc08a2c9ca0
CVC parse
  tag=CV Certificate, cl=1, cmpnd=true, len=403
    tag=Certificate Body, cl=1, cmpnd=true, len=331
      tag=Certificate Profile Identifier, cl=1, cmpnd=false, len=1
      tag=Certification Authority Reference, cl=1, cmpnd=false, len=16
      CAR=DENK010141000000
      tag=Public Key, cl=1, cmpnd=true, len=285
        tag=6, cl=0, cmpnd=false, len=10
        tag=1, cl=2, cmpnd=false, len=32
        tag=2, cl=2, cmpnd=false, len=32
        tag=3, cl=2, cmpnd=false, len=32
        tag=4, cl=2, cmpnd=false, len=65
        tag=5, cl=2, cmpnd=false, len=32
        tag=6, cl=2, cmpnd=false, len=65
        tag=7, cl=2, cmpnd=false, len=1
      tag=Certificate Holder Reference, cl=1, cmpnd=false, len=16
      CHR=DENK010141000000
    tag=Signature, cl=1, cmpnd=false, len=64
  tag=Certification Authority Reference, cl=1, cmpnd=false, len=16
  CAR=DENK010141000000
  tag=Signature, cl=1, cmpnd=false, len=64
CVC end

However, the device certificate and the device issuer certificate have structures like this:

class=1, tag=33, compound=true, len=228, fullLen=232, sha1=4bcfcfe3784903f5a6de0d19bc9023484fa86e91
CVC parse
  tag=Certificate Body, cl=1, cmpnd=true, len=157
    tag=Certificate Profile Identifier, cl=1, cmpnd=false, len=1
    tag=Certification Authority Reference, cl=1, cmpnd=false, len=13
    CAR=DEDINK0100001
    tag=Public Key, cl=1, cmpnd=true, len=79
      tag=6, cl=0, cmpnd=false, len=10
      tag=6, cl=2, cmpnd=false, len=65
    tag=Certificate Holder Reference, cl=1, cmpnd=false, len=16
    CHR=DENK010141000000
    tag=Certificate Holder Authorization Template, cl=1, cmpnd=true, len=16
      tag=6, cl=0, cmpnd=false, len=11
      tag=Discretionary Data, cl=1, cmpnd=false, len=1
    tag=Certificate Effective Date, cl=1, cmpnd=false, len=6
    tag=Certificate Expiration Date, cl=1, cmpnd=false, len=6
  tag=Signature, cl=1, cmpnd=false, len=64
CVC end

which I cannot parse simply with sc_pkcs15emu_sc_hsm_decode_cvc(), I get the following error:

sc_card_ctl(*, SC_CARDCTL_SC_HSM_REGISTER_PUBLIC_KEY, *) failed with Required ASN.1 object not found

I could try wrapping them in a CV Certificate and add a fake outer CAR and Signature in order to also use sc_pkcs15emu_sc_hsm_decode_cvc() for them, but that seems a bit convoluted.

What should I do in that regard in order to get the PR into a mergeable state?

@frankmorgner
Copy link
Member

Sorry for the late response.

OpenPACE has both, CVC_d2i_CVC_CERTfor parsing a complete certificate and d2i_CVC_CERT_BODY to parse the body without the actual signature. That would be one solution.

The other one, would be to extend pkcs15-sc-hsm.c to add a function like sc_pkcs15emu_sc_hsm_decode_cvc_body(). This new function can be bootstrapped from sc_pkcs15emu_sc_hsm_decode_cvc() to only parse the certificate's body. Although this adds some duplication, I think it's better to make the CVC (body) parsing explicit instead of parsing some tags by hand.

/* return CAR */
strncpy((char*) carstr, (const char*) car, taglen);
LOG_FUNC_RETURN(ctx, SC_SUCCESS);
}
Copy link
Member

Choose a reason for hiding this comment

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

The idea is not to parse ASN.1 by hand. Please check what object is missing and why.

return r;
}

static void get_CHR(char *chrstr, int is_cvc, sc_context_t *ctx, const u8 *buf, size_t buflen)
Copy link
Member

Choose a reason for hiding this comment

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

What would be a good way to use EF_C_DevAut, ideally without PIN entry?

The basic idea is in sc-hsm-tool to

  1. force a sc-hsm card driver, as done here and
  2. then cast card->drv_data to struct sc_hsm_private_data * and use its EF_C_DevAut if it's available.

Additionally, you may want to transfer ownership of a newly read EF_C_DevAut to the driver for re-use. You don't need to perform CA to do this.

@frankmorgner
Copy link
Member

@frankbraun , you've put a lot of effort into this and I think it would be useful. However, there has been some inactivity and now there are merge conflicts, would you card to look at this again? Do you need some help regarding the comments above?

@nickaknudson
Copy link

I'm interested in finishing this pull request. Aside from merge conflicts can someone help outline what work remains to be done? Was it just the two comments from Jan 17 2020?

@frankmorgner
Copy link
Member

I'm interested in finishing this pull request. Aside from merge conflicts can someone help outline what work remains to be done? Was it just the two comments from Jan 17 2020?

Yes, indeed. The main problem is maintainability. You should use the CVC capabilities we already have. If I remember correctly, the rest of the code looked OK.

@charredlot
Copy link
Contributor

hi, also interested in this functionality. i rebased this branch and tweaked it to share code with sc_pkcs15emu_sc_hsm_decode_cvc for the CVC parsing here: https://github.com/charredlot/OpenSC/tree/pka

@nickaknudson looks like we ran into this at the same time, so if you have something ready, i'd be happy to defer. otherwise it'd be convenient to have this in upstream soon, so I'll make a new PR if there's no objections?

@charredlot
Copy link
Contributor

created #2301 in the interest of moving this forward

@frankmorgner
Copy link
Member

closed with #2301

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.

None yet

6 participants