Search code examples
rsalibgcrypt

Why does gcrypt say to recalculate the coefficient of an RSA key when converting from SSL format to gcrypt?


The documentation for libgcrypt says:

An RSA private key is described by this S-expression:

(private-key
  (rsa
    (n n-mpi)
    (e e-mpi)
    (d d-mpi)
    (p p-mpi)
    (q q-mpi)
    (u u-mpi)))

...and...

p-mpi
    RSA secret prime p. 
q-mpi
    RSA secret prime q with p < q. 
u-mpi
    Multiplicative inverse u = p^{-1} mod q. 

...and...

Note that OpenSSL uses slighly different parameters: q < p and u = q^{-1} mod p.
To use these parameters you will need to swap the values and recompute u.
Here is example code to do this:

  if (gcry_mpi_cmp (p, q) > 0)
  {
      gcry_mpi_swap (p, q);
      gcry_mpi_invm (u, p, q);
  }

If in one p is the smaller prime and in the other q is the smaller prime, and given that the two equations are identical save for exchanging p and q, is it really necessary to have to recompute u? Is it not sufficient just to exchange p and q?

As a side question, I am curious why gcrypt doesn't use the same values as the PKCS#1 encoding:

     RSAPrivateKey ::= SEQUENCE {
         version           Version,
         modulus           INTEGER,  -- n
         publicExponent    INTEGER,  -- e
         privateExponent   INTEGER,  -- d
         prime1            INTEGER,  -- p
         prime2            INTEGER,  -- q
         exponent1         INTEGER,  -- d mod (p-1)
         exponent2         INTEGER,  -- d mod (q-1)
         coefficient       INTEGER,  -- (inverse of q) mod p
         otherPrimeInfos   OtherPrimeInfos OPTIONAL
     }

o  modulus is the RSA modulus n.
o  publicExponent is the RSA public exponent e.
o  privateExponent is the RSA private exponent d.
o  prime1 is the prime factor p of n.
o  prime2 is the prime factor q of n.
o  exponent1 is d mod (p - 1).
o  exponent2 is d mod (q - 1).
o  coefficient is the CRT coefficient q^(-1) mod p.

Solution

  • The answer is that recalculating "u" is irrelevant. Simply swap the usage of "p" and "q" and it all works.

    As a general comment on gcrypt, the asymmetric cryptography APIs are terrible. Truly abysmal.

    • There is no support for loading keys from file in ANY format.

    • There is no support to simply encrypt/decrypt a buffer. Instead you need to convert the buffer to an MPI before you can then convert it to an S-expression. After encryption you need to unwind the resulting S-expression to get the right piece and then call yet another function to get at the data itself. Decryption requires slightly more complexity in creating the S-expression to decrypt from a buffer, but retrieving the data is only one function call.

    • The parameters to the S-expression for the private key do not match the values in standard PKCS#1 format (although as covered by this question and answer, conversion is fairly easy). Why not?

    During the course of my investigations I discovered that there is another GNU encryption library. Why they maintain two I have no idea. The other is called "nettle" and is much better:

    *) It uses the GMP library for multi-precision integers, rather than having its own type as gcrypt does (mpi_t).

    *) It supports loading keys from files in a variety of formats (I used it as the basis for my own code to load keys for use with gcrypt).

    *) It supports conversion from various formats (PEM->DER, DER->Sexp).

    *) It supports a variety of symmetric encryption algorithms and modes.

    *) It supports asymmetric encryption/decryption/signing/verification.

    I didn't actually use it so I cannot really comment on the APIs usability, but from what I saw it was generally much much better.

    I don't really know the background on nettle, but I do wonder if it was created simply because gcrypt's API is so awful and they would rather start over than enhance gcrypt.