Signing and Verifying of a public key with a DSA master key

View: New views
1 Messages — Rating Filter:   Alert me  

Signing and Verifying of a public key with a DSA master key

by Thomas Mangold :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

Dear OpenSSL Gurus,

For my master thesis, im struggling about three weeks now with the
openssl signing and verifying. I even compiled the library in debug mode
to understand how it works.
I'm using OpenSSL 0.9.7 on Fedora 7 i686

I generated a pair of master DSA keys on the command line as follows:
$ openssl dsaparam -out master.params -genkey 128
Generating DSA parameters, 128 bit long prime
This could take some time
...............+...........+++++++++++++++++++++++++++++++++++++++++++++++++++*
....+.+...............+......................+...+++++++++++++++++++++++++++++++++++++++++++++++++++*

$ openssl gendsa -out master_priv.pem -des3 master.params
Generating DSA key, 512 bits
Enter PEM pass phrase:
Verifying - Enter PEM pass phrase:

$ openssl dsa -in master.params -out master_pub.pem -pubout
read DSA key
writing DSA key

The passphrase for this example is simply "secret".
I read the master keys with PEM_read_DSAPrivateKey() and
PEM_read_DSA_PUBKEY() in the two DSA structures:
DSA *master_priv_key, *master_pub_key;

Here my master_private.pem:
-----BEGIN DSA PRIVATE KEY-----
Proc-Type: 4,ENCRYPTED
DEK-Info: DES-EDE3-CBC,53888FF0ECB4D8C6

sTS3cY82L+1y2duj6Sq9seKzPTJmfc84uzaYkC90/H8GQaniHjDcs4RlO2wDk2mt
TVYBNKOOQd1XoTKrZ4ureyOX5i7Jw9XintSm2Wgj8Hd0VuN05LuA3JxHsXOf89bK
NC3QlskUQJf6a4U1MTlB4Bg8rVjve9AfN+jcwnjSKB/vy3xeddv1oyikcKgtl63x
DuFnDdPtNEprHKD/adDlgh5EoiUc5gAiyWSGlH3Loj0XWTtTe7kmWfs8JNqavyaQ
dVeXj9AWPJ2/2f3t4EPHYHZwUXzc/wcjpJmEnS9qH9lJvWICIeM4ByS8LudPjAw1
+WpA6/pjljQuNr6eKtG6bg==
-----END DSA PRIVATE KEY-----

and here my master_pub.pem:
-----BEGIN PUBLIC KEY-----
MIHwMIGoBgcqhkjOOAQBMIGcAkEAzsZuyZ3CDqMCDR+cKkcEzhrVMU+8xGfX2wnd
pxD0kZ2AMvzGLXllAqTTvRnjC+u4dAcTsnRXDkQLIZ/ZH0lIDQIVAIaxw2UXhOke
578H1v35K7CkkDB5AkBD14zYMMsWrcRZSK8Whoj5dtZDhikN2zhzCzdfSG9odDa9
rZVSlRb4FkVRdFMR7gnpKb2DreKt8U13OH5f36jOA0MAAkAsIOOQi9VzY1Gtt16P
C2v9Z2w8mDwYCGYgCk0i4B6e2sVfTeArIOhx0bp/IaQ7Wu2/WreVGWN5nV2mHHb1
GZ+b
-----END PUBLIC KEY-----

My KEY structure to manage the keys loos like:
typedef struct key {
   char email[EMAIL_BUF_LEN];
   DSA *dsa;                /* public key of the user */
   unsigned char sig_buf[EVP_MAX_MD_SIZE];  /* buffer for the signature
of the public key */
   unsigned int sig_len;                    /* length of the serialised
signature in net format */
   time_t last_use;
   int flags;
   hostid_t *pend;
   int num_req;
   struct key *prev;
   struct key *next;
} KEY;

The two functions for signing and verifying look as follows, but there
is a log of logging information to understand
how it works.
int pers_signKey(KEY *pubkey) {

   EVP_MD_CTX mdctx;         /* context for the message digest
containing the hash value of the public key to sign */
   EVP_PKEY *pkey;           /* envelope key opject containing the DSA
key */
   unsigned char key_buf[DSA_BUF_LEN];   /* buffer to put the serialised
public key */
   unsigned int buf_len;     /* variable to store the key length */
   unsigned char *ptr;       /* a pointer to move over the buffers in
the openssl framework */

   if (!((master_priv_key) && (is_regauth))) {
      log(LOG_ERR, "pers_signKey --> no private key to sign or not a
registration authority.\n");
      return DSA_ERROR;
   }

   /* get rid of old signatures */
   if (pubkey->sig_len)
      pubkey->sig_len = 0;
   memset(pubkey->sig_buf, 0, EVP_MAX_MD_SIZE);

   /* serialise the public key to sign in key_buf
      The pointer on this buffer must be a local variable because
      it will be incremented during the procedure  pointing to
      the address behind key_buf + buf_len */
   /* NOTE i2d_DSAPublicKey seems somehow to free the memory of key_buf
      but it is used again to update the messigae digest context
      anyway it is not longer possible to free it after this call */
   ptr = key_buf;
   if ((buf_len = i2d_DSAPublicKey(pubkey->dsa, &ptr)) < 0) {
      init_logSslErrors("pers_signKey");
      return DSA_ERROR;
   }

   if (buf_len > DSA_BUF_LEN) {
      log(LOG_ERR, "pers_signKey --> serialised key exeeds DSA_BUF_LEN
%d / %d.\n", buf_len, DSA_BUF_LEN);
      return DSA_ERROR;
   }

   /* initialise the context for hashing with the SHA1 algorithm */
   if (!(EVP_SignInit(&mdctx, EVP_dss1()))) {
      init_logSslErrors("pers_signKey (EVP_DigestInit)");
      return DSA_ERROR;
   }

   /* update the context with the serialised key to hash */
   if (!(EVP_SignUpdate(&mdctx, key_buf, buf_len))) {
      init_logSslErrors("pers_signKey (EVP_DigestUpdate)");
      return DSA_ERROR;
   }

   /* create the envelope key */
   if (!(pkey = EVP_PKEY_new())) {
      init_logSslErrors("pers_signKey (EVP_PKEY_new)");
      return DSA_ERROR;
   }

   /* put the private master key into the envelope, don't use
EVP_PKEY_assign_DSA
   or you loose the DSA key too on freeing the EVP_PKEY */
   if (!(EVP_PKEY_set1_DSA(pkey, master_priv_key))) {
      init_logSslErrors("pers_signKey (EVP_PKEY_set1_DSA)");
      EVP_PKEY_free(pkey);
      return DSA_ERROR;
   }

   /* sign the hashvalue of the key */
   if (!(EVP_SignFinal(&mdctx, pubkey->sig_buf, &pubkey->sig_len, pkey))) {
      init_logSslErrors("pers_signKey (EVP_DigestFinal)");
      pubkey->sig_len = 0;
      EVP_PKEY_free(pkey);
      return DSA_ERROR;
   }

   if (pubkey->sig_len > EVP_MAX_MD_SIZE) {
      log(LOG_ERR, "pers_signKey --> serialised signature exeeds
EVP_MAX_MD_SIZE %d / %d.\n", pubkey->sig_len, EVP_MAX_MD_SIZE);
      EVP_PKEY_free(pkey);
      return DSA_ERROR;
   }

   if (loglevel == LOG_DEBUG) {

      DSA_SIG *sig;
      char *bn_buf;

      sig = NULL;
      ptr = pubkey->sig_buf;
      sig = d2i_DSA_SIG(&sig, &ptr, pubkey->sig_len);

      if ((bn_buf = BN_bn2dec(sig->r))) {
         log(LOG_DEBUG, "pers_signKey --> signature r (%d bytes): %s\n",
strlen(bn_buf), bn_buf);
         OPENSSL_free(bn_buf);
      }

      if ((bn_buf = BN_bn2dec(sig->s))) {
         log(LOG_DEBUG, "pers_signKey --> signature s (%d bytes): %s\n",
strlen(bn_buf), bn_buf);
         OPENSSL_free(bn_buf);
      }

      if ((bn_buf = BN_bn2dec(host_priv_key->p))) {
         log(LOG_DEBUG, "pers_signKey --> private key p (%d bytes):
%s\n", strlen(bn_buf), bn_buf);
         OPENSSL_free(bn_buf);
      }

      if ((bn_buf =  BN_bn2dec(host_priv_key->q))) {
         log(LOG_DEBUG, "pers_signKey --> private key q (%d bytes):
%s\n", strlen(bn_buf), bn_buf);
         OPENSSL_free(bn_buf);
      }

      if ((bn_buf = BN_bn2dec(host_priv_key->g))) {
         log(LOG_DEBUG, "pers_signKey --> private key g (%d bytes):
%s\n", strlen(bn_buf), bn_buf);
         OPENSSL_free(bn_buf);
      }

      if ((bn_buf = BN_bn2dec(host_priv_key->priv_key))) {
         log(LOG_DEBUG, "pers_signKey --> private key x (%d bytes):
%s\n", strlen(bn_buf), bn_buf);
         OPENSSL_free(bn_buf);
      }

   }

   EVP_PKEY_free(pkey);
   log(LOG_INFO, "pers_signKey --> key %s signed.\n", pubkey->email);
   return DSA_ACCEPT;
}

int pers_verifyKey(KEY *pubkey){

   EVP_MD_CTX mdctx;         /* context for the message digest
containing the hash value of the public key to verify */
   EVP_PKEY *pkey;           /* a pointer on the envelope for the DSA key */
   unsigned char key_buf[DSA_BUF_LEN];   /* buffer containing the
serialised public key */
   unsigned int buf_len;     /* variable to store the key length */
   int ret;                  /* the return value of the signature check */
   const unsigned char *ptr;       /* a pointer on the keybuffer */

   pubkey->flags &= ~IS_VERIFIED;

   /* begin of real procedure */
   if (!pubkey->sig_len) {
      log(LOG_ERR,"pers_verifyKey --> can't verify - no signature in key
%s.\n", pubkey->email);
      return DSA_ERROR;
   }

   /* serialise the public key to verify in key_buf
   memory is allocated and must be freed on error
   and when finished */
   if (!pubkey->dsa) {
      log(LOG_ERR,"pers_verifyKey --> can't verify - no dsa structure in
key %s.\n", pubkey->email);
      return DSA_ERROR;
   }

   /* serialise the public key to verify in key_buf
      The pointer on this buffer must be a local variable because
      it will be incremented during the procedure pointing to
      the address behind key_buf + buf_len */
   ptr = key_buf;
   if ((buf_len = i2d_DSAPublicKey(pubkey->dsa, &ptr)) < 0) {
      init_logSslErrors("pers_verifyKey (i2d_DSAPublicKey)");
      return DSA_ERROR;
   }

   if (buf_len > DSA_BUF_LEN) {
      log(LOG_ERR, "pers_verifyKey --> serialised key exeeds DSA_BUF_LEN
%d / %d.\n", buf_len, DSA_BUF_LEN);
      return DSA_ERROR;
   }

   /* initialise the context for hashing with the SHA1 algorithm */
   if (!(EVP_VerifyInit(&mdctx, EVP_dss1()))) {
      init_logSslErrors("pers_verifyKey (EVP_DigestInit)");
      return DSA_ERROR;
   }

   /* update the context with the serialised key to hash */
   if (!(EVP_VerifyUpdate(&mdctx, key_buf, buf_len))) {
      init_logSslErrors("pers_verifyKey (EVP_DigestUpdate)");
      return DSA_ERROR;
   }

   if (loglevel == LOG_DEBUG) {

      DSA_SIG *sig;
      char *bn_buf;

      sig = NULL;
      ptr = pubkey->sig_buf;
      sig = d2i_DSA_SIG(&sig, &ptr, pubkey->sig_len);

      if ((bn_buf = BN_bn2dec(sig->r))) {
         log(LOG_DEBUG, "pers_verifyKey --> signature r (%d bytes):
%s\n", strlen(bn_buf), bn_buf);
         OPENSSL_free(bn_buf);
      }

      if ((bn_buf = BN_bn2dec(sig->s))) {
         log(LOG_DEBUG, "pers_verifyKey --> signature s (%d bytes):
%s\n", strlen(bn_buf), bn_buf);
         OPENSSL_free(bn_buf);
      }

      if ((bn_buf = BN_bn2dec(master_pub_key->p))) {
         log(LOG_DEBUG, "pers_verifyKey --> public key p (%d bytes):
%s\n", strlen(bn_buf), bn_buf);
         OPENSSL_free(bn_buf);
      }

      if ((bn_buf = BN_bn2dec(master_pub_key->q))) {
         log(LOG_DEBUG, "pers_verifyKey --> public key q (%d bytes):
%s\n", strlen(bn_buf), bn_buf);
         OPENSSL_free(bn_buf);
      }

      if ((bn_buf = BN_bn2dec(master_pub_key->g))) {
         log(LOG_DEBUG, "pers_verifyKey --> public key g (%d bytes):
%s\n", strlen(bn_buf), bn_buf);
         OPENSSL_free(bn_buf);
      }

      if ((bn_buf = BN_bn2dec(master_pub_key->pub_key))) {
         log(LOG_DEBUG, "pers_verifyKey --> public key y (g^x) (%d
bytes): %s\n", strlen(bn_buf), bn_buf);
         OPENSSL_free(bn_buf);
      }
   }

   /* create the envelope key */
   if (!(pkey = EVP_PKEY_new())) {
      init_logSslErrors("pers_verifyKey (EVP_PKEY_new)");
      return DSA_ERROR;
   }

   /* put the public master key into the envelope, don't use
EVP_PKEY_assign_DSA
   or you loose the DSA key too on freeing the EVP_PKEY */
   if (!(EVP_PKEY_set1_DSA(pkey, master_pub_key))) {
      init_logSslErrors("pers_verifyKey (EVP_PKEY_set1_DSA)");
      EVP_PKEY_free(pkey);
      return DSA_ERROR;
   }

   ret = EVP_VerifyFinal(&mdctx, pubkey->sig_buf, pubkey->sig_len, pkey);

   if (ret == DSA_ERROR) {
      pubkey->sig_len = 0;
      init_logSslErrors("pers_verifyKey (EVP_DigestFinal)");
      EVP_PKEY_free(pkey);
      return ret;
   }

   if (ret == DSA_ACCEPT) {
      pubkey->flags |= IS_VERIFIED;
      pubkey->flags &= ~FROM_FILE;
      log(LOG_INFO, "pers_verifyKey --> key %s verified.\n", pubkey->email);
   }

   if (ret == DSA_REJECT) {
      pubkey->sig_len = 0;
      log(LOG_INFO, "pers_verifyKey --> key %s rejected.\n", pubkey->email);
   }

   EVP_PKEY_free(pkey);
   return ret;
}

The logging output of this signing and verifying example is:
21:19:33 pers_signKey --> signature r (48 bytes):
748579810289355699838500268556918127256339752467
21:19:34 pers_signKey --> signature s (48 bytes):
688280662998673456050651939583557489974822517632
21:19:34 pers_signKey --> private key p (155 bytes):
10829692190232787463565838956086206137190348560761908594796749951079376565632981279403369050926864048144229783329236491486421335210727979083380759271655437
21:19:35 pers_signKey --> private key q (48 bytes):
768969016486072524144558170934065416490814812281
21:19:35 pers_signKey --> private key g (154 bytes):
3553173476319000480323849172700953872902743131825941145971353799053654197254289715921211086624613172598558713446689292640459457466917659881927304867195086
21:19:36 pers_signKey --> private key x (48 bytes):
374320894898160993779827061043104060008118291894
21:19:38 pers_signKey --> key thomas.mangold@... signed.
21:19:50 pers_verifyKey --> signature r (48 bytes):
748579810289355699838500268556918127256339752467
21:19:51 pers_verifyKey --> signature s (48 bytes):
688280662998673456050651939583557489974822517632
21:19:51 pers_verifyKey --> public key p (155 bytes):
10829692190232787463565838956086206137190348560761908594796749951079376565632981279403369050926864048144229783329236491486421335210727979083380759271655437
21:19:52 pers_verifyKey --> public key q (48 bytes):
768969016486072524144558170934065416490814812281
21:19:53 pers_verifyKey --> public key g (154 bytes):
3553173476319000480323849172700953872902743131825941145971353799053654197254289715921211086624613172598558713446689292640459457466917659881927304867195086
21:19:54 pers_verifyKey --> public key y (g^x) (154 bytes):
2311195631463402811034457671992662441661460498286972902253094605910543108710915798218219055877819519505658487943208491707682304736152687567269924687159195
21:20:27 pers_verifyKey --> key thomas.mangold@... rejected.

There are no errors but the return value of EVP_VerifyFinal() is always
0. The big numbers computed are matching but I could not calculate the
algorithm by hand due to integer overflow on every system i tried.

Has anyone an idea, what I'm doing wrong?
thank you for helping
Thomas Mangold
______________________________________________________________________
OpenSSL Project                                 http://www.openssl.org
User Support Mailing List                    openssl-users@...
Automated List Manager                           majordomo@...