-
Notifications
You must be signed in to change notification settings - Fork 712
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
OpenPGP card ECC SSH keys problem #1906
Comments
Did you check https://support.nitrokey.com/? |
Tried to do some gooling first. There was another issue here back in 2016 which was resolved with updates to both opensc and openssh to support ECC. |
The pubkey above does not have the curve OID. Could be bug in I don't have a Nitro card, but a PIV card with ECC:
Try something like this to see the public key with parameters:
|
@dengert You seem to be right
It can't get EC parameters. Should I report it to Nitrokey's repo issues? |
@alex-nitrokey Do you have any comments on this? From what I can tell from https://shop.nitrokey.com/shop/product/nitrokey-storage-2-56
https://www.nitrokey.com/documentation/frequently-asked-questions-faq#which-gnupg,-opensc-and-libccid-versions-are-required says: "OpenSC 0.18 or above should work." Problem could be in card-openpgp.c It has a list of supported ATRs, which map to 4 @unb9rn an OpenSC debug log would help answer many of these questions to see why the curve is not being set. It should be set in |
@dengert I'm not sure if there is verbosity levels higher than 9, hope these two logs may help. |
card-openpgp.c does not have a lot of debugging. So it is not easy to see what it doing. Looking at read-ssh-key.log at lines 1319-1333
It reads in all the DOs, then later uses read_binary to parse this looking for path=006e007300c2, 006e007300c3 and 006e007300c1 which are "4.4.3.7 Algorithm Attributes" in V3.0 C2 06 12 2B 81 04 00 23 C3 06 13 2B 81 04 00 23 C1 06 13 2B 81 04 00 23 0x12 = 18 ECDH, 0x13 = 19 ECDSA for PSO:CDS and INT-AUT These all say the keys are: ansix9p521r1, OID = {1.3.132.0.35} = '2B81040023' But the real problem is the meaning of a "pubkey" RSA has no parameters, EC has a parameter, the OID of the algorithm. For some uses the parameter is required and this is the case for EC. pkcs15-tool in read_ssh_key and read_public_key will call: In either case, the pubkey->u.ec.params.id should have the OID of the curve. Adding to the pkcs15-tool --verbose would show more debugging info. |
Looks like adding --verbose somehow conflicts with debug info. After adding this key there is almost no debug info (tried both opensc.conf and overriding via OPENSC_DEBUG=9). All info that remains:
Removing --verbose key spews debug info as before... |
Does you card have certificates for the 3 keys? (See second fix below) pkcs15-tool, will use either the public key, returned by the card, or if not found it will try and use the certificate and obtain the public key SPKI from the certificate. EC public keys have a parameter, usually the curve OID. The SPKI from a certificate will always have one. OpenPGP specs for 3.0 and above refer to RFC 6637 Section 9. Encoding of Public and Private Keys says: "Algorithm-Specific Fields for ECDSA keys: a variable-length field containing a curve OID" The OpenSC card-openpgp.c does not add the curve OID to a public key as requested by pkcs15-tool so pkcs15-tool says "Unsupported curve". The Algorithm attributes DOs, C1, C2 and C3 for the 3 key contain the content of the OID so the curve OID can be derived. Card-openpgp.c will parse when needed but when returning a public key to a caller it does not add these to the response. There are at least 2 ways to fix this:
|
Unfortunately, the only objects on my card are 3 GPG keys (one key and two subkeys) |
pkcs15-tool need OID from pubkey->u.ec.params.id and field length (even this can be deduced from OID) in pubkey->u.ec.params.field_length. Probably the easiest way is ad small part of code to card-openpgp.c:
pubkey.u.ec.ecpointQ.len can be used so select correct OID and field lenght from structure:
The data from proposed structure is then used to set pubkey->u.ec.params.id and pubkey->u.ec.params.field_length. I do not have OpenPGP device, can only guess what is in pubkey_blob->len and if this corresponds to values in proposed structure .. Of course, this is not a clean solution, but it will work. |
cc @Nitrokey |
Hi! I am not familiarized with the OpenSC internals, but I can support testing for this case. If you have any dev branch with the patch just let me know. |
You can not really deduce the curve name from the pubkey.nu.ec.ecpointQ.len. For example, current card-openpgp.c card-openpgp.c uses The
A developer really needs a V3.0 card with ECC support to make and debug these changes. |
I may have a fix. It using the OpenPGP "Algorithm Attributes" to add the curve OID ,then uses sc_pkcs15_fix_ec_parameters to look up the name curve so sc_encode_pubkey will pass it to pkcs15-tool. |
…SC#1906 The EC Parameters are the way the EC curve is presented to the outside world, and in most cases is present in a matching certificate in the SPKI. card-openpgp.c is modified to add the EC named_curve to the PKCS15 public key. OpenPGP specs only provide this via the "Algorithm Attributes" for the 3 keys via tags C1, C2 and C3 These contain the OID for the EC curve, which is then mapped by sc_pkcs15_fix_ec_parameters to a EC Curve name. This is the first attempt to fix this problem without having a token that supports OpenGPG V3.3, so it will need additional testing and debugging by developers who have such a token. On branch openpgp-ec-pub-curve Changes to be committed: modified: card-openpgp.c
I can jump in on Thursday to help/debug this issue as I added most of the ECC related stuff in card-openpgp.c. I will see if I can improve the code regarding the |
@unb9rn You did not say how you created the EC keys on the token. And you also said: " I am using NIST P-384 curve." But the The DOs C1, C2, and C3 i.e. the Algorithm attributes in the debug output says these have have the OID for ansix9p521r1. Have you tried both? Did you reinitialize the card? |
@szszszsz @unb9rn @alex-nitrokey have a look at #1911 which may fix the problem. Other questions:
|
@dengert oh, I'm sorry. Yes, the card was initialized with GPG (the keys were generated on my machine, not on the card itsef. then i moved them to the card). At that point I've found another bug either in GPG or in Nitrokey's firmware, which allowed me to push P-384 keys, but not P-521 to the card, but I decided to re-check and it turned out that P-521 keys could still be generated on the card itself. So, answering your question-yes, I've re-initialized the card with GPG, generating the keys directly on the card. Regarding Brainpool curves, I've tried them too, they seem to work, but it looks like SSH is incompatible with Brainpool curves, so i decided to use the NIST one. |
Thanks. Your experience brings up an issue. It appears that the curve to be used, is specified in the "Algorithm Attribute" and is the curve used by the card. From the debug output the curve OID is for ansiX9p521r1 and the pubkey read from the card is for this size curve. The pub key read from the card (DO A400 lines 1622-1630 in the debug log) does not have an OID parameter. So the curve is not stored with the pubkey . So the card does not look at the pubkey DO, but does not say the curve OID could be stored in the pubkey for use by the caller. It is not clear who sets "Algorithm Attribute" for each key. A card could change the "Algorithm Attribute" as part of a generate or import a key operation or the calling application sets the "Algorithm Attribute" and then does a generate key or import. (I am not an OpenPGP expert, does anyone know how this is done?) OpenPGP-smart-card-application-3.3.1 says: "From this recommendation the following curves should be used for an OpenPGP smart card At least one of this curves shall be supported by an OpenPGP smart card if EC is available." Note that ansix9p384r1 and brainpoolP384r1 have the same pubkey size. So the curve must be available to the card as well as the application. |
I've patched 0.20 with card-openpgp.c and tried re-building. It compiled, but the problem still persists... @szszszsz Can you re-check and confirm please? |
Did you get a opensc debug log? If not, can you run GDB on it. You may have to compile with debugging. One way to do this is to set CFLAGS and LDFLAGS before running configure:
then rebuild. The to debug you can the run something like this:
It should stop at the breakpoint, and you can get a I am interested if it got to this if statement,, then do If the if at 1716 failed you will be at 1735, and you will have to step through it, by doing These instructions may be a little off, as I don't have a card that will go through code. But the "if" at 1721 is probably the problem. |
Nope, there is no such function in logs. Will rebuild and install gdb tonight. Thanks! |
I think it doesn't enter pgp_get_blob after |
I think we are close. (Your line numbers and mine may not match. the gdb list command can be used to show a number of source lines which helpful.) 1735 in card-openpgp.c The key_info looks good, id=3, algorithm =0x13 and oid = {1, 3, 132, 0, 35} and key_length=521 match what is expected from the card. (ecpoint and ecpoint_len are not filled in yet.) (gdb) next The above looks good too. algorithm=SC_ALGORITHM_EC, (named_curve not filled in yet), the der value looks good, gdb only showed the first 3 bytes is asci with escaped octal But in my line 1723 "&key_info.u.ec.oid) > 0) {" has an error. I am assuming that you are expecting the 521 EC key. I also need to do some cleanup, as p15pubkey may have some memory leaks. |
Hm, I've changed the condition on 1723 to |
The "gdb-fix-ec-params.log" does not show the output of Note that GDB stops before executing the line at the breakpoint so you can examine input. I also notice that when you run gdb, it can not find the source code. But gdb has a way to add the path to source directories. Google for: gnu gdb set source path. (My build environment has been setup over years of programming and I am not sure what trick I used to get gdb to always find the source to work. ) I also have |
Sorry, the log wasn't full. I've added the code to help you understand line-by-line, placed a breakpoint on sc_pkcs15_fix_ec_parameters, tried running it with some steps where it passes the conditional and enters loop and then ran it to finish. It returned -1408 and didn't update named_curve... Maybe I'm not helpful enough =) |
…SC#1906 The EC Parameters are the way the EC curve is presented to the outside world, and in most cases is present in a matching certificate in the SPKI. card-openpgp.c is modified to add the EC named_curve to the PKCS15 public key. OpenPGP specs only provide this via the "Algorithm Attributes" for the 3 keys via tags C1, C2 and C3 These contain the OID for the EC curve, which is then mapped by sc_pkcs15_fix_ec_parameters to a EC Curve name. This is the first attempt to fix this problem without having a token that supports OpenGPG V3.3, so it will need additional testing and debugging by developers who have such a token. On branch openpgp-ec-pub-curve Changes to be committed: modified: card-openpgp.c
I just pushed a minor fix which you have plus some debugging that would end up in opensc debug log. I also rebased on current master from today. There are a few changes to card-openpgp.c in current master that could make a difference. Yes you are very helpful. What makes OpenSC is difficult, is not every developer has all the cards. So the only way to debug is like what we are doing now. Can you run again with what you have or with the new patch based on the current master. print p15pubkey.u.ec.params The GDB x command can also be helpful with binary data. Something like the -1408 is SC_ERROR_NOT_SUPPORTED. |
opensc-new.log |
I think I have it. Used the wrong encoding routine. |
It seems like it finally updates named_curve, yet the final result is still "unsupported curve"... |
Loos like the card-openpgp.c is working. Maybe set Also try without gdb: |
Sorry for being late for the party. |
Seems like it doesn't pass curve_name further... |
I agree. I think I see the problem I should have spotted this early. If that works, you can also try removing line 1724, because The term pubkey is used loosely. RSA does not have any parameters, most if not all other public keys have parameters. The SPKI in a certificate include the DER of a public key with parameters. If that does not solve it, a few things to try. The before |
Sorry, change to: |
And we have a lift-off! =) |
Almost everything is good. Both RSA and ECC are functioning, yet something is wrong- after returning SSH RSA key pkcs15-tool crashes with
|
I have been able to test the card-openpgp.c with RSA keys with a Yubico 4 and to test the pkcs15-tool.c with EC using a PIV card. But can not test both pkcs15-tool and card-openpgp.c with the same card. Thanks to Nitrokey, I should have a Nitrokey Pro in 2 to 3 weeks. so I can reproduce the problem. If you are willing to continue debugging and testing possible fixes:
You can leave out the |
Haven't tried disabling function call yet, but here you go, valgrind logs with gdb bt |
I think I messed up the valgrind and gdb commands so it did not run the command. ==38500== TO DEBUG THIS PROCESS USING GDB: start GDB like this I said: "You can leave out the gdb --args if you want." looks like just leave out the "gdb --args" |
Looks like the double free comes while OpenSC is cleaning up and the card-openpgp.c is cleaning up the blob tree. But it does not show what blob is being freed. If valgrind can't point it out, we may need to use gdb and set break at card-openpgp.c:1166 i.e. Then at each break:
looks like it fails after 2 times, as the iteration count starts at 99 then goes down so we can catch which one is failing then try and figure out how that blob was created, and how the data could be double freed. |
valgrind.log |
Did this crash? Is this some intermittent issue? Can you live with these changes and get SSH working for a few weeks, until I get the Nitrokey. What commands did you use to create the keys on the token? Any other developers who have a OpenPGP device with EC keys but no certificates? |
Nope, it didn't. |
It takes like 30 iterations to crash |
Pushed another fix for dumb mistake onto #1911
Line 1736 should be sc_pkcs15_erase_pubkey would free p15pubkey.u.rsa.exponent.data because it was not set to NULL, then later when trying to free blob info = ... <pgp34_objects+800>, id = 130 (0x82)
Thus the same data would be freed. |
Everything seems to work fine, both RSA and EC. Thanks a lot! |
…SC#1906 The EC Parameters are the way the EC curve is presented to the outside world, and in most cases is present in a matching certificate in the SPKI. card-openpgp.c is modified to add the EC named_curve to the PKCS15 public key. OpenPGP specs only provide this via the "Algorithm Attributes" for the 3 keys via tags C1, C2 and C3 These contain the OID (not DER encoded) for the EC curve. PKCS15 has two ways to encode a "pubkey" as it was originally written for RSA. But other algorithms have parameters. X509 certificates encode the public key in the SPKI and PKIX requires the parameters to be in the SPKI. PKCS15 allows for using a SPKI as source for a public key. pgp_get_pubkey_pem will return the DER encoded RSA pubkey as before by calling sc_pkcs15_encode_pubkey pgp_get_pubkey_pem will return the DER encoded EC pubkey with parameters by calling sc_pkcs15_encode_pubkey_as_spki which calls sc_pkcs15_fix_ec_parameters internally to map DER encoded OID to named_curve. For readability, "sc_pkcs15_pubkey_t pubkey;" definitions are changed to "sc_pkcs15_pubkey_t p15pubkey;" sc_pkcs15_erase_pubkey is used to avoid memory leaks. On branch openpgp-ec-pub-curve Date: Tue Jan 21 09:43:56 2020 -0600 Changes to be committed: modified: src/libopensc/card-openpgp.c
The EC Parameters are the way the EC curve is presented to the outside world, and in most cases is present in a matching certificate in the SPKI. card-openpgp.c is modified to add the EC named_curve to the PKCS15 public key. OpenPGP specs only provide this via the "Algorithm Attributes" for the 3 keys via tags C1, C2 and C3 These contain the OID (not DER encoded) for the EC curve. PKCS15 has two ways to encode a "pubkey" as it was originally written for RSA. But other algorithms have parameters. X509 certificates encode the public key in the SPKI and PKIX requires the parameters to be in the SPKI. PKCS15 allows for using a SPKI as source for a public key. pgp_get_pubkey_pem will return the DER encoded RSA pubkey as before by calling sc_pkcs15_encode_pubkey pgp_get_pubkey_pem will return the DER encoded EC pubkey with parameters by calling sc_pkcs15_encode_pubkey_as_spki which calls sc_pkcs15_fix_ec_parameters internally to map DER encoded OID to named_curve. For readability, "sc_pkcs15_pubkey_t pubkey;" definitions are changed to "sc_pkcs15_pubkey_t p15pubkey;" sc_pkcs15_erase_pubkey is used to avoid memory leaks. On branch openpgp-ec-pub-curve Date: Tue Jan 21 09:43:56 2020 -0600 Changes to be committed: modified: src/libopensc/card-openpgp.c
Oh, I'm sorry for the delay, was out of town. Great to see it was merged. I can confirm everything works fine with binary build from master. Thanks! |
Problem Description
I am trying to set up my Nitrokey Storage 2 to connect to remote hosts with ECC. I am using NIST P-384 curve. I have no problems at listing all the keys:
But then, there is a problem on exporting the third (auth) key in SSH form:
NIST P-384 should be supported by both this token and OpenSSH. Of course, this problem persists for two other keyslots.
ssh-keygen returns similar problems:
Of course, I have no problems just reading any of those keys:
I also have another Nitrokey with RSA keys from my work, it looks like it works perfectly with ssh and opensc_pkcs11 module.
Steps to reproduce
The text was updated successfully, but these errors were encountered: