Completed the RNG implementaion switching.
[libgcrypt.git] / src / fips.c
index 0083177..8f36797 100644 (file)
 #include <config.h>
 #include <stdio.h>
 #include <stdlib.h>
-#include <assert.h>
 #include <errno.h>
 #include <unistd.h>
-
-/* #include <dlfcn.h>  /\* FIXME:  GNU only *\/ */
+#ifdef ENABLE_HMAC_BINARY_CHECK
+# include <dlfcn.h> 
+#endif
 
 #include "g10lib.h"
 #include "ath.h"
 #include "cipher-proto.h"
+#include "hmac256.h"
 
 /* The states of the finite state machine used in fips mode.  */
 enum module_states 
@@ -85,14 +86,14 @@ _gcry_initialize_fips_mode (int force)
           fips_noreturn ();
         }
       /* If not in fips mode an assert is sufficient.  */
-      assert (!done);
+      gcry_assert (!done);
     }
   done = 1;
 
   /* If the calling applicatione explicitly requested fipsmode, do so.  */
   if (force)
     {
-      assert (!no_fips_mode_required);
+      gcry_assert (!no_fips_mode_required);
       goto leave;
     }
 
@@ -106,7 +107,7 @@ _gcry_initialize_fips_mode (int force)
      allowed.  */
   if ( !access ("/etc/gcrypt/fips140.force", F_OK) )
     {
-      assert (!no_fips_mode_required);
+      gcry_assert (!no_fips_mode_required);
       goto leave;
     }
 
@@ -124,7 +125,7 @@ _gcry_initialize_fips_mode (int force)
           {
             /* System is in fips mode.  */
             fclose (fp);
-            assert (!no_fips_mode_required);
+            gcry_assert (!no_fips_mode_required);
             goto leave;
           }
         fclose (fp);
@@ -418,13 +419,79 @@ run_pubkey_selftests (void)
 }
 
 
-/* Run self-tests for the random number generator.  Return 0 on
+/* Run self-tests for the random number generator.  Returns 0 on
    success. */
 static int
 run_random_selftests (void)
 {
+  gpg_error_t err;
+
+  err = _gcry_random_selftest (reporter);
+  reporter ("random", 0, NULL, err? gpg_strerror (err):NULL);
+  
+  return !!err;
+}
 
+/* Run an integrity check on the binary.  Returns 0 on success.  */
+static int
+check_binary_integrity (void)
+{
+#ifdef ENABLE_HMAC_BINARY_CHECK
+  gpg_error_t err;
+  Dl_info info;
+  unsigned char digest[32];
+  int dlen;
+  char *fname = NULL;
+  const char key[] = "What am I, a doctor or a moonshuttle conductor?";
+  
+  if (!dladdr ("gcry_check_version", &info))
+    err = gpg_error_from_syserror ();
+  else
+    {
+      dlen = _gcry_hmac256_file (digest, sizeof digest, info.dli_fname,
+                                 key, strlen (key));
+      if (dlen < 0)
+        err = gpg_error_from_syserror ();
+      else if (dlen != 32)
+        err = gpg_error (GPG_ERR_INTERNAL);
+      else
+        {
+          FILE *fp;
+  
+          fname = gcry_malloc (strlen (info.dli_fname) + 5 + 1 );
+          if (!fname)
+            err = gpg_error_from_syserror ();
+          else
+            {
+              strcpy (stpcpy (fname, info.dli_fname), ".hmac");
+              fp = fopen (fname, "rb");
+              if (!fp)
+                err = gpg_error_from_syserror ();
+              else
+                {
+                  char buffer[33];
+                  int n;
+
+                  /* We expect a file of exactly 32 bytes.  Consider
+                     the self-test failed if this is not the case or
+                     if it does not match the just computed HMAC.  */
+                  if ((n=fread (buffer, 1, 33, fp)) != 32
+                      || memcmp (digest, buffer, 32) )
+                    err = gpg_error (GPG_ERR_SELFTEST_FAILED);
+                  else
+                    err = 0;
+
+                  fclose (fp);
+                }
+            }
+        }
+    }
+  reporter ("binary", 0, fname, err? gpg_strerror (err):NULL);
+  gcry_free (fname);
+  return !!err;
+#else
   return 0;
+#endif
 }
 
 
@@ -436,15 +503,6 @@ _gcry_fips_run_selftests (void)
   
   fips_new_state (STATE_SELFTEST);
 
-/*   { */
-/*     Dl_info info; */
-
-/*     if (dladdr ("gcry_check_version", &info)) */
-/*       log_info ("DL_info:  fname=`%s'\n", */
-/*                 info.dli_fname); */
-/*   } */
-
-
   if (run_cipher_selftests ())
     goto leave;
 
@@ -460,6 +518,11 @@ _gcry_fips_run_selftests (void)
   if (run_random_selftests ())
     goto leave;
 
+  /* Now check the integrity of the binary.  We do this this after
+     having checked the HMAC code.  */
+  if (check_binary_integrity ())
+    goto leave;
+
   /* All selftests passed.  */
   result = STATE_OPERATIONAL;
 
@@ -537,6 +600,7 @@ fips_new_state (enum module_states new_state)
       
     case STATE_ERROR:
       if (new_state == STATE_SHUTDOWN
+          || new_state == STATE_FATALERROR
           || new_state == STATE_INIT)
         ok = 1;
       break;