Certificate requests generated from card-based ECDSA keys are incorrectly marked as RSA-signed
Open, LowPublic

Description

When using gpgsm --gen-key to create a certificate request from a card-stored ECDSA key, the resulting certificate request has an incorrect Signature Algorithm identifier (sha256WithRSAEncryption instead of the expected ecdsa-with-SHA256), even though the rest of the request seems fine.

This can be seen by dumping the request with OpenSSL's req command:

$ openssl req -in gpgsm-generated-csr.pem -noout -text
Certificate Request:
    Data:
        Version: 0 (0x0)
        Subject: C=UK, L=London, CN=Alice Smith
        Subject Public Key Info:
            Public Key Algorithm: id-ecPublicKey
                Public-Key: (256 bit)
                pub: 
                    04:83:90:8f:ad:a3:2f:8a:da:39:6c:fc:5b:ac:aa:
                    f8:d8:d7:02:ee:be:5f:f4:c3:78:e7:34:25:33:73:
                    0e:bf:98:32:bf:0f:76:a1:1f:6d:19:ad:1a:20:3b:
                    5d:95:43:d3:f5:fc:9f:e2:f2:97:e1:32:35:36:2b:
                    7a:6e:05:d8:b0
                ASN1 OID: prime256v1
                NIST CURVE: P-256
        Attributes:
        Requested Extensions:
            X509v3 Subject Alternative Name: 
                email:alice@example.org
            X509v3 Key Usage: critical
                Digital Signature, Non Repudiation
    Signature Algorithm: sha256WithRSAEncryption
         2a:e6:aa:da:dd:36:bd:de:61:88:18:0b:35:25:3c:cf:70:94:
         30:b5:b0:4e:e1:1e:05:49:40:14:55:37:1c:aa:16:19:01:ea:
         c2:b6:6b:57:be:37:24:59:5c:b9:e7:bb:5b:2a:b3:67:26:4c:
         d7:6c:1a:ae:e3:de:8b:6a:10:ae

Basically gpgsm --gen-key does not currently support working with card-based keys of any other type than RSA. The assumption that card-based keys are RSA-only is hardcoded at several places in GpgSM code, such as in function gpgsm_scd_pksign (sm/call-agent.c):

/* Create an S-expression from it which is formatted like this:
   "(7:sig-val(3:rsa(1:sSIGBUFLEN:SIGBUF)))" Fixme: If a card ever
   creates non-RSA keys we need to change this. */

or in function transform_sigval (sm/misc.c):

if (!tok || toklen != 3 || memcmp ("rsa", tok, toklen))
  return gpg_error (GPG_ERR_WRONG_PUBKEY_ALGO)

This assumption should be revised now that cards compliant with version 3+ of the OpenPGP card specification are available.

gouttegd created this task.Aug 6 2018, 12:00 AM
gouttegd created this object in space S1 Public.

Since it seems there is a renewed interest in adding ECC support to GpgSM (as indicated by the T4098 feature request), I would like to write down here more details about this task.

First, the problem described here is actually more complicated than I initially thought (isn’t that always the case?): it’s not only that the Signature Algorithm identifier is incorrect, the signature value is wrong as well.

Second, this problem actually affects both ECDSA and EdDSA keys. (For EdDSA keys, this comes in addition to the problem described in T4013 about the generation of the SPKI value. Both problems need to be fixed to be able to generate a certificate request for a token-based EdDSA key.)

Third, a quick review of what, in my understanding, are the sources of the problem and what needs to be done:

gpgsm_scd_pksign
The first problem is in the gpgsm_scd_pksign function of GpgSM (sm/call-agent.c), which unconditionally generates a S-expression for a RSA signature:

(sig-val(rsa(s S-MPI)))

According to libgcrypt’s documentation, the S-expression for a ECDSA signature should be instead

(sig-val(ecdsa(r R-MPI)(s S-MPI)))

and the S-expression for a EdDSA signature should be

(sig-val(eddsa(r R-MPI)(s S-MPI)))

where R-MPI and S-MPI are the results of the E(C|d)DSA signing operation.

transform_sigval
The second problem is in GpgSM’s transform_sigval function (sm/misc.c). This function takes as input a S-expression representing a signature and transforms it by replacing the literal name of the signature algorithm by the corresponding OID. Currently, this function only supports RSA signatures.

Presumably, for a ECDSA signature, this function should insert one of the following OIDs specified by RFC 5758 (depending on the hashing algorithm used for the signature):

  • 1.2.840.10045.4.3.1 for a ECDSA-with-sha224 signature,
  • 1.2.840.10045.4.3.2 for a ECDSA-with-sha256 signature,
  • 1.2.840.10045.4.3.3 for a ECDSA-with-sha384 signature, or
  • 1.2.840.10045.4.3.4 for a ECDSA-with-sha512 signature.

For a EdDSA signature, according to the RFC 8419 the algorithm identifier should be the same as the one used in the SPKI (i.e., 1.3.101.112). There is no need for a set of identifiers coupling the signature algorithm and the hashing algorithm, since EdDSA signatures always use SHA-512.

Libksba
The third and probably most important problem is in Libksba’s ksba_certreq_set_sig_val function (src/certreq.c). This function is currently unable to handle a sig-val S-expression containing more than one MPI. This is fine for a RSA signature, but obviously not for a E(C|d)DSA signature which is is made of two MPIs.

For a ECDSA signature, the value generated by this function should be constructed as a SEQUENCE of two INTEGER values (the r MPI and the s MPI), whereas for a EdDSA signature, according to RFC 8410, §6, the value should be constructed simply as the concatenation of the two MPIs. This would probably require a complete overhaul of the ksba_certreq_set_sig_val function.

werner added a subscriber: werner.Feb 14 2019, 7:31 AM

Thanks for that summary.