Implemented more self-tests.
authorWerner Koch <wk@gnupg.org>
Thu, 11 Sep 2008 10:38:04 +0000 (10:38 +0000)
committerWerner Koch <wk@gnupg.org>
Thu, 11 Sep 2008 10:38:04 +0000 (10:38 +0000)
cipher/ChangeLog
cipher/dsa.c
cipher/rsa.c
doc/ChangeLog
doc/Makefile.am
doc/gcrypt.texi
src/ChangeLog
src/fips.c
tests/basic.c

index b3412f8..6fe98e3 100644 (file)
@@ -1,3 +1,14 @@
+2008-09-11  Werner Koch  <wk@g10code.com>
+
+       * rsa.c (_gcry_rsa_decrypt): Return an error instead of calling
+       BUG in case of a practically impossible condition.
+       (sample_secret_key, sample_public_key): New.
+       (selftest_sign_1024, selftest_encr_1024): New.
+       (selftests_rsa): Implement tests.
+       * dsa.c (sample_secret_key, sample_public_key): New.
+       (selftest_sign_1024): New.
+       (selftests_dsa): Implement tests.
+
 2008-09-09  Werner Koch  <wk@g10code.com>
 
        * hmac-tests.c (selftests_sha1): Add tests.
index daafdde..02cc1ce 100644 (file)
@@ -1,4 +1,4 @@
-/* dsa.c  -  DSA signature scheme
+/* dsa.c - DSA signature scheme
  * Copyright (C) 1998, 2000, 2001, 2002, 2003,
  *               2006, 2008  Free Software Foundation, Inc.
  *
@@ -47,6 +47,45 @@ typedef struct
 } DSA_secret_key;
 
 
+/* A sample 1024 bit DSA key used for the selftests.  */
+static const char sample_secret_key[] =
+"(private-key"
+" (dsa"
+"  (p #00AD7C0025BA1A15F775F3F2D673718391D00456978D347B33D7B49E7F32EDAB"
+"      96273899DD8B2BB46CD6ECA263FAF04A28903503D59062A8865D2AE8ADFB5191"
+"      CF36FFB562D0E2F5809801A1F675DAE59698A9E01EFE8D7DCFCA084F4C6F5A44"
+"      44D499A06FFAEA5E8EF5E01F2FD20A7B7EF3F6968AFBA1FB8D91F1559D52D8777B#)"
+"  (q #00EB7B5751D25EBBB7BD59D920315FD840E19AEBF9#)"
+"  (g #1574363387FDFD1DDF38F4FBE135BB20C7EE4772FB94C337AF86EA8E49666503"
+"      AE04B6BE81A2F8DD095311E0217ACA698A11E6C5D33CCDAE71498ED35D13991E"
+"      B02F09AB40BD8F4C5ED8C75DA779D0AE104BC34C960B002377068AB4B5A1F984"
+"      3FBA91F537F1B7CAC4D8DD6D89B0D863AF7025D549F9C765D2FC07EE208F8D15#)"
+"  (y #64B11EF8871BE4AB572AA810D5D3CA11A6CDBC637A8014602C72960DB135BF46"
+"      A1816A724C34F87330FC9E187C5D66897A04535CC2AC9164A7150ABFA8179827"
+"      6E45831AB811EEE848EBB24D9F5F2883B6E5DDC4C659DEF944DCFD80BF4D0A20"
+"      42CAA7DC289F0C5A9D155F02D3D551DB741A81695B74D4C8F477F9C7838EB0FB#)"
+"  (x #11D54E4ADBD3034160F2CED4B7CD292A4EBF3EC0#)))";
+/* A sample 1024 bit DSA key used for the selftests (public only).  */
+static const char sample_public_key[] = 
+"(public-key"
+" (dsa"
+"  (p #00AD7C0025BA1A15F775F3F2D673718391D00456978D347B33D7B49E7F32EDAB"
+"      96273899DD8B2BB46CD6ECA263FAF04A28903503D59062A8865D2AE8ADFB5191"
+"      CF36FFB562D0E2F5809801A1F675DAE59698A9E01EFE8D7DCFCA084F4C6F5A44"
+"      44D499A06FFAEA5E8EF5E01F2FD20A7B7EF3F6968AFBA1FB8D91F1559D52D8777B#)"
+"  (q #00EB7B5751D25EBBB7BD59D920315FD840E19AEBF9#)"
+"  (g #1574363387FDFD1DDF38F4FBE135BB20C7EE4772FB94C337AF86EA8E49666503"
+"      AE04B6BE81A2F8DD095311E0217ACA698A11E6C5D33CCDAE71498ED35D13991E"
+"      B02F09AB40BD8F4C5ED8C75DA779D0AE104BC34C960B002377068AB4B5A1F984"
+"      3FBA91F537F1B7CAC4D8DD6D89B0D863AF7025D549F9C765D2FC07EE208F8D15#)"
+"  (y #64B11EF8871BE4AB572AA810D5D3CA11A6CDBC637A8014602C72960DB135BF46"
+"      A1816A724C34F87330FC9E187C5D66897A04535CC2AC9164A7150ABFA8179827"
+"      6E45831AB811EEE848EBB24D9F5F2883B6E5DDC4C659DEF944DCFD80BF4D0A20"
+"      42CAA7DC289F0C5A9D155F02D3D551DB741A81695B74D4C8F477F9C7838EB0FB#)))";
+
+
+
+\f
 static gcry_mpi_t gen_k (gcry_mpi_t q);
 static void test_keys (DSA_secret_key *sk, unsigned qbits);
 static int check_secret_key (DSA_secret_key *sk);
@@ -539,23 +578,103 @@ _gcry_dsa_get_nbits (int algo, gcry_mpi_t *pkey)
      Self-test section.
  */
 
+static const char *
+selftest_sign_1024 (gcry_sexp_t pkey, gcry_sexp_t skey)
+{
+  static const char sample_data[] = 
+    "(data (flags pkcs1)"
+    " (hash sha1 #a0b1c2d3e4f500102030405060708090a1b2c3d4#))";
+  static const char sample_data_bad[] = 
+    "(data (flags pkcs1)"
+    " (hash sha1 #a0b1c2d3e4f510102030405060708090a1b2c3d4#))";
+
+  const char *errtxt = NULL;
+  gcry_error_t err;
+  gcry_sexp_t data = NULL;
+  gcry_sexp_t data_bad = NULL;
+  gcry_sexp_t sig = NULL;
+
+  err = gcry_sexp_sscan (&data, NULL,
+                         sample_data, strlen (sample_data));
+  if (!err)
+    err = gcry_sexp_sscan (&data_bad, NULL, 
+                           sample_data_bad, strlen (sample_data_bad));
+  if (err)
+    {
+      errtxt = "converting data failed";
+      goto leave;
+    }
+
+  err = gcry_pk_sign (&sig, data, skey);
+  if (err)
+    {
+      errtxt = "signing failed";
+      goto leave;
+    }
+  err = gcry_pk_verify (sig, data, pkey);
+  if (err)
+    {
+      errtxt = "verify failed";
+      goto leave;
+    }
+  err = gcry_pk_verify (sig, data_bad, pkey);
+  if (gcry_err_code (err) != GPG_ERR_BAD_SIGNATURE)
+    {
+      errtxt = "bad signature not detected";
+      goto leave;
+    }
+
+
+ leave:
+  gcry_sexp_release (sig);
+  gcry_sexp_release (data_bad);
+  gcry_sexp_release (data);
+  return errtxt;
+}
+
 
 static gpg_err_code_t
 selftests_dsa (selftest_report_func_t report)
 {
   const char *what;
   const char *errtxt;
-  
-  what = "low-level";
-  errtxt = NULL; /*selftest ();*/
+  gcry_error_t err;
+  gcry_sexp_t skey = NULL;
+  gcry_sexp_t pkey = NULL;
+
+  /* Convert the S-expressions into the internal representation.  */
+  what = "convert";
+  err = gcry_sexp_sscan (&skey, NULL, 
+                         sample_secret_key, strlen (sample_secret_key));
+  if (!err)
+    err = gcry_sexp_sscan (&pkey, NULL, 
+                           sample_public_key, strlen (sample_public_key));
+  if (err)
+    {
+      errtxt = gcry_strerror (err);
+      goto failed;
+    }
+
+  what = "key consistency";
+  err = gcry_pk_testkey (skey);
+  if (err)
+    {
+      errtxt = gcry_strerror (err);
+      goto failed;
+    }
+
+  what = "sign";
+  errtxt = selftest_sign_1024 (pkey, skey);
   if (errtxt)
     goto failed;
 
-  /* FIXME:  need more tests.  */
-
+  gcry_sexp_release (pkey);
+  gcry_sexp_release (skey);
   return 0; /* Succeeded. */
 
  failed:
+  gcry_sexp_release (pkey);
+  gcry_sexp_release (skey);
   if (report)
     report ("pubkey", GCRY_PK_DSA, what, errtxt);
   return GPG_ERR_SELFTEST_FAILED;
index 9a7b94d..386adbc 100644 (file)
@@ -1,4 +1,4 @@
-/* rsa.c  -  RSA function
+/* rsa.c - RSA implementation
  * Copyright (C) 1997, 1998, 1999 by Werner Koch (dd9jn)
  * Copyright (C) 2000, 2001, 2002, 2003, 2008 Free Software Foundation, Inc.
  *
@@ -52,6 +52,38 @@ typedef struct
 } RSA_secret_key;
 
 
+/* A sample 1024 bit RSA key used for the selftests.  */
+static const char sample_secret_key[] =
+"(private-key"
+" (rsa"
+"  (n #00e0ce96f90b6c9e02f3922beada93fe50a875eac6bcc18bb9a9cf2e84965caa"
+"      2d1ff95a7f542465c6c0c19d276e4526ce048868a7a914fd343cc3a87dd74291"
+"      ffc565506d5bbb25cbac6a0e2dd1f8bcaab0d4a29c2f37c950f363484bf269f7"
+"      891440464baf79827e03a36e70b814938eebdc63e964247be75dc58b014b7ea251#)"
+"  (e #010001#)"
+"  (d #046129f2489d71579be0a75fe029bd6cdb574ebf57ea8a5b0fda942cab943b11"
+"      7d7bb95e5d28875e0f9fc5fcc06a72f6d502464dabded78ef6b716177b83d5bd"
+"      c543dc5d3fed932e59f5897e92e6f58a0f33424106a3b6fa2cbf877510e4ac21"
+"      c3ee47851e97d12996222ac3566d4ccb0b83d164074abf7de655fc2446da1781#)"
+"  (p #00e861b700e17e8afe6837e7512e35b6ca11d0ae47d8b85161c67baf64377213"
+"      fe52d772f2035b3ca830af41d8a4120e1c1c70d12cc22f00d28d31dd48a8d424f1#)"
+"  (q #00f7a7ca5367c661f8e62df34f0d05c10c88e5492348dd7bddc942c9a8f369f9"
+"      35a07785d2db805215ed786e4285df1658eed3ce84f469b81b50d358407b4ad361#)"
+"  (u #304559a9ead56d2309d203811a641bb1a09626bc8eb36fffa23c968ec5bd891e"
+"      ebbafc73ae666e01ba7c8990bae06cc2bbe10b75e69fcacb353a6473079d8e9b#)))";
+/* A sample 1024 bit RSA key used for the selftests (public only).  */
+static const char sample_public_key[] = 
+"(public-key"
+" (rsa"
+"  (n #00e0ce96f90b6c9e02f3922beada93fe50a875eac6bcc18bb9a9cf2e84965caa"
+"      2d1ff95a7f542465c6c0c19d276e4526ce048868a7a914fd343cc3a87dd74291"
+"      ffc565506d5bbb25cbac6a0e2dd1f8bcaab0d4a29c2f37c950f363484bf269f7"
+"      891440464baf79827e03a36e70b814938eebdc63e964247be75dc58b014b7ea251#)"
+"  (e #010001#)))";
+
+
+
+\f
 static void test_keys (RSA_secret_key *sk, unsigned nbits);
 static gpg_err_code_t generate (RSA_secret_key *sk,
                                 unsigned int nbits, unsigned long use_e,
@@ -585,12 +617,11 @@ _gcry_rsa_decrypt (int algo, gcry_mpi_t *result, gcry_mpi_t *data,
                          GCRY_STRONG_RANDOM);
       gcry_mpi_mod (r, r, sk.n);
 
-      /* Actually it should be okay to skip the check for equality
-        with either p or q here.  */
-
-      /* Calculate inverse of r.  */
-      if (! gcry_mpi_invm (ri, r, sk.n))
-       BUG ();
+      /* Calculate inverse of r.  It practically impossible that the
+         follwing test fails, thus we do not add code to release
+         allocated resources.  */
+      if (!gcry_mpi_invm (ri, r, sk.n))
+       return GPG_ERR_INTERNAL;
     }
 
   if (! (flags & PUBKEY_FLAG_NO_BLINDING))
@@ -730,23 +761,179 @@ compute_keygrip (gcry_md_hd_t md, gcry_sexp_t keyparam)
      Self-test section.
  */
 
+static const char *
+selftest_sign_1024 (gcry_sexp_t pkey, gcry_sexp_t skey)
+{
+  static const char sample_data[] = 
+    "(data (flags pkcs1)"
+    " (hash sha1 #11223344556677889900aabbccddeeff10203040#))";
+  static const char sample_data_bad[] = 
+    "(data (flags pkcs1)"
+    " (hash sha1 #11223344556677889900aabbccddeeff80203040#))";
+
+  const char *errtxt = NULL;
+  gcry_error_t err;
+  gcry_sexp_t data = NULL;
+  gcry_sexp_t data_bad = NULL;
+  gcry_sexp_t sig = NULL;
+
+  err = gcry_sexp_sscan (&data, NULL,
+                         sample_data, strlen (sample_data));
+  if (!err)
+    err = gcry_sexp_sscan (&data_bad, NULL, 
+                           sample_data_bad, strlen (sample_data_bad));
+  if (err)
+    {
+      errtxt = "converting data failed";
+      goto leave;
+    }
+
+  err = gcry_pk_sign (&sig, data, skey);
+  if (err)
+    {
+      errtxt = "signing failed";
+      goto leave;
+    }
+  err = gcry_pk_verify (sig, data, pkey);
+  if (err)
+    {
+      errtxt = "verify failed";
+      goto leave;
+    }
+  err = gcry_pk_verify (sig, data_bad, pkey);
+  if (gcry_err_code (err) != GPG_ERR_BAD_SIGNATURE)
+    {
+      errtxt = "bad signature not detected";
+      goto leave;
+    }
+
+
+ leave:
+  gcry_sexp_release (sig);
+  gcry_sexp_release (data_bad);
+  gcry_sexp_release (data);
+  return errtxt;
+}
+
+
+static const char *
+selftest_encr_1024 (gcry_sexp_t pkey, gcry_sexp_t skey)
+{
+  const char *errtxt = NULL;
+  gcry_error_t err;
+  const unsigned int nbits = 1000; /* Encrypt 1000 random bits.  */
+  gcry_mpi_t value = NULL;
+  gcry_sexp_t plain = NULL;
+  gcry_sexp_t encr  = NULL;
+  gcry_sexp_t decr  = NULL;
+  gcry_mpi_t  decr_value = NULL;
+  gcry_sexp_t tmplist = NULL;
+
+  /* Create plain text.  */
+  value = gcry_mpi_new (nbits);
+  gcry_mpi_randomize (value, nbits, GCRY_WEAK_RANDOM);
+  
+  err = gcry_sexp_build (&plain, NULL, "(data (flags raw) (value %m))", value);
+  if (err)
+    {
+      errtxt = "converting data failed";
+      goto leave;
+    }
+
+  /* Encrypt and decrypt.  */
+  err = gcry_pk_encrypt (&encr, plain, pkey);
+  if (err)
+    {
+      errtxt = "encrypt failed";
+      goto leave;
+    }
+  err = gcry_pk_decrypt (&decr, encr, skey);
+  if (err)
+    {
+      errtxt = "decrypt failed";
+      goto leave;
+    }
+
+  /* Extract decrypted data.  The output of gcry_pk_decrypt depends on
+     whether a flags lists occurs in its input data.  Because we
+     passed the output of gcry_pk_encrypt directly to gcry_pk_decrypt,
+     such a flag value won't be there as of today.  To be prepared for
+     future changes we take care of it anyway.  */
+  tmplist = gcry_sexp_find_token (decr, "value", 0);
+  if (tmplist)
+    decr_value = gcry_sexp_nth_mpi (tmplist, 1, GCRYMPI_FMT_USG);
+  else
+    decr_value = gcry_sexp_nth_mpi (decr, 0, GCRYMPI_FMT_USG);
+  if (!decr_value)
+    {
+      errtxt = "decrypt returned no value";
+      goto leave;
+    }
+
+  if (gcry_mpi_cmp (value, decr_value))
+    {
+      errtxt = "mismatch";
+      goto leave;
+    }
+
+ leave:
+  gcry_sexp_release (tmplist);
+  gcry_mpi_release (decr_value);
+  gcry_sexp_release (decr);
+  gcry_sexp_release (encr);
+  gcry_sexp_release (plain);
+  gcry_mpi_release (value);
+  return errtxt;
+}
+
 
 static gpg_err_code_t
 selftests_rsa (selftest_report_func_t report)
 {
   const char *what;
   const char *errtxt;
+  gcry_error_t err;
+  gcry_sexp_t skey = NULL;
+  gcry_sexp_t pkey = NULL;
   
-  what = "low-level";
-  errtxt = NULL; /*selftest ();*/
+  /* Convert the S-expressions into the internal representation.  */
+  what = "convert";
+  err = gcry_sexp_sscan (&skey, NULL, 
+                         sample_secret_key, strlen (sample_secret_key));
+  if (!err)
+    err = gcry_sexp_sscan (&pkey, NULL, 
+                           sample_public_key, strlen (sample_public_key));
+  if (err)
+    {
+      errtxt = gcry_strerror (err);
+      goto failed;
+    }
+
+  what = "key consistency";
+  err = gcry_pk_testkey (skey);
+  if (err)
+    {
+      errtxt = gcry_strerror (err);
+      goto failed;
+    }
+
+  what = "sign";
+  errtxt = selftest_sign_1024 (pkey, skey);
   if (errtxt)
     goto failed;
 
-  /* FIXME:  need more tests.  */
+  what = "encrypt";
+  errtxt = selftest_encr_1024 (pkey, skey);
+  if (errtxt)
+    goto failed;
 
+  gcry_sexp_release (pkey);
+  gcry_sexp_release (skey);
   return 0; /* Succeeded. */
 
  failed:
+  gcry_sexp_release (pkey);
+  gcry_sexp_release (skey);
   if (report)
     report ("pubkey", GCRY_PK_RSA, what, errtxt);
   return GPG_ERR_SELFTEST_FAILED;
index 7ecfd5b..6355766 100644 (file)
@@ -1,3 +1,8 @@
+2008-09-11  Werner Koch  <wk@g10code.com>
+
+       * gcrypt.texi (Public-Key Subsystem Architecture): Explain RSA
+       blinding.
+
 2008-09-08  Marcus Brinkmann  <marcus@g10code.com>
 
        * gcrypt.texi: Some typos fixed.
index 7862110..f251064 100644 (file)
@@ -63,9 +63,9 @@ online: gcrypt.html gcrypt.pdf gcrypt.info
          cp gcrypt.info gcrypt.html/; \
        else \
           rsync -v gcrypt.pdf gcrypt.info \
-               $${user}@cvs.gnupg.org:webspace/manuals/ ; \
+               $${user}@trithemius.gnupg.org:webspace/manuals/ ; \
         fi ; \
        cd gcrypt.html ; \
         rsync -vr --exclude='.svn' .  \
-         $${user}@cvs.gnupg.org:webspace/manuals/gcrypt$${dashdevel}/ 
+         $${user}@trithemius.gnupg.org:webspace/manuals/gcrypt$${dashdevel}/ 
 
index c5209b2..26a9f69 100644 (file)
@@ -2273,7 +2273,7 @@ function checks that this data actually can be used with the given key,
 does the padding and encrypts it.
 
 If the function could successfully perform the encryption, the return
-value will be 0 and a new S-expression with the encrypted result is
+value will be 0 and a new S-expression with the encrypted result is
 allocated and assigned to the variable at the address of @var{r_ciph}.
 The caller is responsible to release this value using
 @code{gcry_sexp_release}.  In case of an error, an error code is
@@ -4608,9 +4608,20 @@ level code (@file{cipher/pubkey.c}).  Thus the internal interface
 between the algorithm modules and the high level functions passes data
 in a custom format.  The interface to the modules is published
 (@file{gcrypt-modules.h}) so that it can used to register external
-implementations of algorithms with Libgcrypt.  However, for some algorithms this
-module interface is to limited and thus for the internal modules an
-extra interface is sometimes used to convey more information.
+implementations of algorithms with Libgcrypt.  However, for some
+algorithms this module interface is to limited and thus for the
+internal modules an extra interface is sometimes used to convey more
+information.
+
+By default Libgcrypt uses a blinding technique for RSA decryption to
+mitigate real world timing attacks over a network: Instead of using
+the RSA decryption directly, a blinded value @math{y = x r^{e} \bmod n}
+is decrypted and the unblinded value @math{x' = y' r^{-1} \bmod n}
+returned.  The blinding value @math{r} is a random value with the size
+of the modulus @math{n} and generated with @code{GCRY_STRONG_RANDOM}
+random level.
+
+
 
 
 @node Symmetric Encryption Subsystem Architecture
index 57826a2..582026b 100644 (file)
@@ -1,3 +1,8 @@
+2008-09-11  Werner Koch  <wk@g10code.com>
+
+       * fips.c (_gcry_fips_run_selftests): Run random tests before the
+       pubkey tests.
+
 2008-09-05  Werner Koch  <wk@g10code.com>
 
        * gcrypt.h.in (GCYRCTL_SELFTEST): New.
index 73a5816..0402e62 100644 (file)
@@ -538,10 +538,12 @@ _gcry_fips_run_selftests (void)
   if (run_hmac_selftests ())
     goto leave;
 
-  if (run_pubkey_selftests ())
+  /* Run random tests before the pubkey tests because the latter
+     require random.  */
+  if (run_random_selftests ())
     goto leave;
 
-  if (run_random_selftests ())
+  if (run_pubkey_selftests ())
     goto leave;
 
   /* Now check the integrity of the binary.  We do this this after
index 97ac2d9..b51f8f8 100644 (file)
@@ -1375,7 +1375,7 @@ check_one_hmac (int algo, const char *data, int datalen,
   gcry_md_close (hd);
 
   p = gcry_md_read (hd2, algo);
-  if (0 == p)
+  if (!p)
     fail("algo %d, hmac gcry_md_read failed\n", algo);
 
   if (memcmp (p, expect, mdlen))