Add signature support in eet.
authorcedric <cedric>
Mon, 8 Sep 2008 09:19:57 +0000 (09:19 +0000)
committercedric <cedric@7cbeb6ba-43b4-40fd-8cce-4c39aea84d33>
Mon, 8 Sep 2008 09:19:57 +0000 (09:19 +0000)
You can use eet -s to sign a eet file and eet -c to check its signature. The
current code doesn't check any certificate trust path, only if the signature
match the certificate and that the certificate could be loaded. If that's not
the case, eet_open will fail. If the file is not signed, eet_open will just
continue to succeed.

git-svn-id: http://svn.enlightenment.org/svn/e/trunk/eet@35882 7cbeb6ba-43b4-40fd-8cce-4c39aea84d33

configure.in
src/bin/eet_main.c
src/lib/Eet.h
src/lib/Eet_private.h
src/lib/Makefile.am
src/lib/eet_cypher.c [new file with mode: 0644]
src/lib/eet_lib.c
src/tests/cert.pem [new file with mode: 0644]
src/tests/eet_suite.c
src/tests/key.pem [new file with mode: 0644]

index ebf301e..21026ee 100644 (file)
@@ -143,6 +143,54 @@ fi
 
 AM_CONDITIONAL(EET_ENABLE_TESTS, test "x${enable_tests}" = "xyes")
 
+dnl Openssl support
+want_openssl="auto"
+have_openssl="no"
+AC_ARG_ENABLE(openssl,
+  [AC_HELP_STRING([--disable-openssl], [disable openssl eet support])],
+  [ want_openssl=$enableval ]
+)
+if test "x$want_openssl" = "xyes" -o "x$want_openssl" = "xauto"; then
+  PKG_CHECK_MODULES(OPENSSL, openssl,
+    [
+      have_openssl="yes"
+      AC_DEFINE(HAVE_OPENSSL, 1, [Have Openssl support])
+    ],
+    [
+      if test "x$use_strict" = "xyes"; then
+        AC_MSG_ERROR([Openssl not found (strict dependencies checking)])
+      fi
+    ])
+fi
+
+dnl Crypto option
+want_cypher="yes"
+have_cypher="no"
+want_signature="yes"
+have_signature="no"
+
+AC_MSG_CHECKING(whether to activate cypher support in eet)
+AC_ARG_ENABLE(cypher,
+  [AC_HELP_STRING([--disable-cypher], [disable cypher support for eet API])],
+  [ want_cypher=$enableval ]
+)
+if test "x$have_openssl" = "xyes" -a "x$want_cypher" = "xyes"; then
+  have_cypher="yes"
+  AC_DEFINE(HAVE_CYPHER, 1, [Have cypher support built in eet])
+fi
+AC_MSG_RESULT($have_cypher)
+
+AC_MSG_CHECKING(whether to activate signature support in eet)
+AC_ARG_ENABLE(signature,
+  [AC_HELP_STRING([--disable-signature], [disable signature file support for eet])],
+  [ want_signature=$enableval ]
+)
+if test "x$have_openssl" = "xyes" -a "x$want_signature" = "xyes"; then
+  have_signature="yes"
+  AC_DEFINE(HAVE_SIGNATURE, 1, [Have signature support for eet file])
+fi
+AC_MSG_RESULT($have_signature)
+
 dnl Coverage
 
 AC_ARG_ENABLE(coverage,
@@ -241,6 +289,10 @@ echo "------------------------------------------------------------------------"
 echo
 echo "Configuration Options Summary:"
 echo
+echo "  Openssl..............: ${have_openssl}"
+echo "    Cypher support.....: ${have_cypher}"
+echo "    Signature..........: ${have_signature}"
+
 echo "  Tests................: ${enable_tests}"
 echo "  Coverage.............: ${enable_coverage}"
 echo
index 539cec8..0db4026 100644 (file)
@@ -200,6 +200,50 @@ do_eet_remove(const char *file, const char *key)
    eet_close(ef);
 }
 
+static void
+do_eet_check(const char *file)
+{
+   Eet_File *ef;
+   const void *der;
+   int der_length;
+
+   ef = eet_open(file, EET_FILE_MODE_READ);
+   if (!ef)
+     {
+       fprintf(stdout, "checking signature of `%s` failed\n", file);
+       exit(-1);
+     }
+
+   der = eet_identity_x509(ef, &der_length);
+
+   eet_identity_certificate_print(der, der_length, stdout);
+
+   eet_close(ef);
+}
+
+static void
+do_eet_sign(const char *file, const char *private_key, const char *public_key)
+{
+   Eet_File *ef;
+   Eet_Key *key;
+
+   ef = eet_open(file, EET_FILE_MODE_READ_WRITE);
+   if (!ef)
+     {
+       fprintf(stdout, "cannot open for read+write: %s.\n", file);
+       exit(-1);
+     }
+
+   key = eet_identity_open(public_key, private_key, NULL);
+
+   fprintf(stdout, "Using the following key to sign `%s`.\n", file);
+   eet_identity_print(key, stdout);
+
+   eet_identity_set(ef, key);
+
+   eet_close(ef);
+}
+
 int
 main(int argc, char **argv)
 {
@@ -214,6 +258,8 @@ main(int argc, char **argv)
               "  eet -i FILE.EET KEY IN-FILE COMPRESS insert data to KEY in FILE.EET from IN-FILE and if COMPRESS is 1, compress it\n"
               "  eet -e FILE.EET KEY IN-FILE COMPRESS insert and encode to KEY in FILE.EET from IN-FILE and if COMPRESS is 1, compress it\n"
               "  eet -r FILE.EET KEY                  remove KEY in FILE.EET\n"
+              "  eet -c FILE.EET                      report and check the signature information of an eet file\n"
+              "  eet -s FILE.EET PRIVATE_KEY PUBLIC_KEY sign FILE.EET with PRIVATE_KEY and attach PUBLIC_KEY as it's certificate\n"
               );
        eet_shutdown();
        return -1;
@@ -250,6 +296,18 @@ main(int argc, char **argv)
      {
        goto help;
      }
+   else if ((!strcmp(argv[1], "-c")) && (argc > 2))
+     {
+       do_eet_check(argv[2]);
+     }
+   else if ((!strcmp(argv[1], "-s")) && (argc > 4))
+     {
+       do_eet_sign(argv[2], argv[3], argv[4]);
+     }
+   else
+     {
+       goto help;
+     }
    eet_shutdown();
    return 0;
 }
index 2f52aab..c528bc2 100644 (file)
@@ -2,6 +2,7 @@
 #define _EET_H
 
 #include <stdlib.h>
+#include <stdio.h>
 
 #ifdef EAPI
 # undef EAPI
@@ -86,12 +87,19 @@ extern "C" {
        EET_ERROR_WRITE_ERROR_FILE_TOO_BIG,
        EET_ERROR_WRITE_ERROR_IO_ERROR,
        EET_ERROR_WRITE_ERROR_OUT_OF_SPACE,
-       EET_ERROR_WRITE_ERROR_FILE_CLOSED
+       EET_ERROR_WRITE_ERROR_FILE_CLOSED,
+       EET_ERROR_MMAP_FAILED,
+       EET_ERROR_X509_ENCODING_FAILED,
+       EET_ERROR_SIGNATURE_FAILED,
+       EET_ERROR_INVALID_SIGNATURE,
+       EET_ERROR_NOT_SIGNED,
+       EET_ERROR_NOT_IMPLEMENTED
      } Eet_Error;
 
    typedef struct _Eet_File                  Eet_File;
    typedef struct _Eet_Dictionary            Eet_Dictionary;
    typedef struct _Eet_Data_Descriptor       Eet_Data_Descriptor;
+   typedef struct _Eet_Key                   Eet_Key;
 
    typedef struct _Eet_Data_Descriptor_Class Eet_Data_Descriptor_Class;
 
@@ -247,6 +255,60 @@ extern "C" {
     */
    EAPI Eet_Error eet_close(Eet_File *ef);
 
+  /**
+   * Callback used to request if needed the password of a private key.
+   *
+   * @since 2.0.0
+   */
+   typedef int (*Eet_Key_Password_Callback)(char *buffer, int size, int rwflag, void *data);
+
+   /**
+    * Create an Eet_Key needed for signing an eet file.
+    *
+    * The certificate should provide the public that match the private key.
+    * No verification is done to ensure that.
+    *
+    * @since 2.0.0
+    */
+   EAPI Eet_Key* eet_identity_open(const char *certificate_file, const char *private_key_file, Eet_Key_Password_Callback cb);
+
+    /**
+     * Close and release all ressource used by an Eet_Key.
+     * An reference counter prevent it from being freed until all file using it are
+     * also closed.
+     *
+     * @since 2.0.0
+     */
+   EAPI void eet_identity_close(Eet_Key *key);
+
+    /**
+     * Set a key to sign a file
+     *
+     * @since 2.0.0
+     */
+   EAPI Eet_Error eet_identity_set(Eet_File *ef, Eet_Key *key);
+
+    /**
+     * Display both private and public key of an Eet_Key.
+     *
+     * @since 2.0.0
+     */
+   EAPI void eet_identity_print(Eet_Key *key, FILE *out);
+
+    /**
+     * Get the x509 der certificate associated with an Eet_File. Will return NULL
+     * if the file is not signed.
+     *
+     * @since 2.0.0
+     */
+   EAPI const void *eet_identity_x509(Eet_File *ef, int *der_length);
+
+   /**
+    * Display the x509 der certificate to out.
+    *
+    * @since 2.0.0
+    */
+   EAPI void eet_identity_certificate_print(const unsigned char *certificate, int der_length, FILE *out);
 
    /**
     * Return a handle to the shared string dictionary of the Eet file
index 7160783..57da2a4 100644 (file)
 # endif
 #endif
 
+#include "config.h"
+
+#ifdef HAVE_OPENSSL
+#include <openssl/evp.h>
+#include <openssl/x509.h>
+#include <openssl/pem.h>
+#endif
+
 typedef struct _Eet_String              Eet_String;
 
 struct _Eet_String
@@ -53,6 +61,15 @@ struct _Eet_Dictionary
   const char   *end;
 };
 
+struct _Eet_Key
+{
+   int          references;
+#ifdef HAVE_SIGNATURE
+   X509               *certificate;
+   EVP_PKEY    *private_key;
+#endif
+};
+
 Eet_Dictionary  *eet_dictionary_add(void);
 void             eet_dictionary_free(Eet_Dictionary *ed);
 int              eet_dictionary_string_add(Eet_Dictionary *ed, const char *string);
@@ -66,6 +83,15 @@ int   _eet_hash_gen(const char *key, int hash_size);
 int   _eet_string_to_double_convert(const char *src, long long *m, long *e);
 void  _eet_double_to_string_convert(char des[128], double d);
 
+const void* eet_identity_check(const void *data_base, unsigned int data_length,
+                              const void *signature_base, unsigned int signature_length,
+                              int *x509_length);
+Eet_Error eet_cypher(void *data, unsigned int size, const char *key, unsigned int length);
+Eet_Error eet_decypher(void *data, unsigned int size, const char *key, unsigned int length);
+Eet_Error eet_identity_sign(FILE *fp, Eet_Key *key);
+void eet_identity_unref(Eet_Key *key);
+void eet_identity_ref(Eet_Key *key);
+
 #ifndef PATH_MAX
 #define PATH_MAX 4096
 #endif
index 0214fa3..7055ffd 100644 (file)
@@ -8,7 +8,8 @@ AM_CPPFLAGS = \
 -DPACKAGE_LIB_DIR=\"$(libdir)\" \
 -DPACKAGE_DATA_DIR=\"$(datadir)/$(PACKAGE)\" \
 @EVIL_CFLAGS@ \
-@COVERAGE_CFLAGS@
+@COVERAGE_CFLAGS@ \
+@OPENSSL_CFLAGS@
 
 include_HEADERS = Eet.h
 
@@ -18,6 +19,7 @@ libeet_la_SOURCES  = \
 eet_lib.c \
 eet_data.c \
 eet_image.c \
+eet_cypher.c \
 eet_dictionary.c \
 eet_utils.c
 
diff --git a/src/lib/eet_cypher.c b/src/lib/eet_cypher.c
new file mode 100644 (file)
index 0000000..dcaab55
--- /dev/null
@@ -0,0 +1,335 @@
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <sys/mman.h>
+
+#ifdef HAVE_SIGNATURE
+# include <openssl/rsa.h>
+# include <openssl/objects.h>
+# include <openssl/err.h>
+# include <openssl/ssl.h>
+# include <openssl/dh.h>
+# include <openssl/dsa.h>
+#endif
+
+#include "Eet.h"
+#include "Eet_private.h"
+
+#define EET_MAGIC_SIGN 0x1ee74271
+
+EAPI Eet_Key*
+eet_identity_open(const char *certificate_file, const char *private_key_file, Eet_Key_Password_Callback cb)
+{
+#ifdef HAVE_SIGNATURE
+   EVP_PKEY *pkey = NULL;
+   X509 *cert = NULL;
+   Eet_Key *key;
+   FILE *fp;
+
+   /* Load the X509 certificate in memory. */
+   fp = fopen(certificate_file, "r");
+   if (!fp) return NULL;
+   cert = PEM_read_X509(fp, NULL, NULL, NULL);
+   fclose(fp);
+   if (!cert) return NULL;
+
+   /* Check the presence of the public key. Just in case. */
+   pkey = X509_get_pubkey(cert);
+   if (!pkey) goto on_error;
+
+   /* Load the private key in memory. */
+   fp = fopen(private_key_file, "r");
+   if (!fp) goto on_error;
+   pkey = PEM_read_PrivateKey(fp, NULL, cb, NULL);
+   fclose(fp);
+   if (!pkey) goto on_error;
+
+   key = malloc(sizeof(Eet_Key));
+   if (!key) goto on_error;
+
+   key->references = 1;
+   key->certificate = cert;
+   key->private_key = pkey;
+
+   return key;
+
+ on_error:
+   if (cert) X509_free(cert);
+   if (pkey) EVP_PKEY_free(pkey);
+#endif
+   return NULL;
+}
+
+EAPI void
+eet_identity_print(Eet_Key *key, FILE *out)
+{
+#ifdef HAVE_SIGNATURE
+   RSA *rsa;
+   DSA *dsa;
+   DH *dh;
+
+   if (!key) return ;
+
+   rsa = EVP_PKEY_get1_RSA(key->private_key);
+   if (rsa)
+     {
+       fprintf(out, "Private key (RSA) :\n");
+       RSA_print_fp(out, rsa, 0);
+     }
+
+   dsa = EVP_PKEY_get1_DSA(key->private_key);
+   if (dsa)
+     {
+       fprintf(out, "Private key (DSA) :\n");
+       DSA_print_fp(out, dsa, 0);
+     }
+
+   dh = EVP_PKEY_get1_DH(key->private_key);
+   if (dh)
+     {
+       fprintf(out, "Private key (DH) :\n");
+       DHparams_print_fp(out, dh);
+     }
+
+   fprintf(out, "Public certificate :\n");
+   X509_print_fp(out, key->certificate);
+#else
+   fprintf(out, "You need to compile signature support in EET.\n");
+#endif
+}
+
+EAPI void
+eet_identity_close(Eet_Key *key)
+{
+#ifdef HAVE_SIGNATURE
+   if (key->references > 0) return ;
+
+   X509_free(key->certificate);
+   EVP_PKEY_free(key->private_key);
+   free(key);
+#endif
+}
+
+void
+eet_identity_ref(Eet_Key *key)
+{
+   if (key == NULL) return ;
+   key->references++;
+}
+
+void
+eet_identity_unref(Eet_Key *key)
+{
+   if (key == NULL) return ;
+   key->references--;
+   eet_identity_close(key);
+}
+
+Eet_Error
+eet_identity_sign(FILE *fp, Eet_Key *key)
+{
+#ifdef HAVE_SIGNATURE
+   unsigned char *x509_der = NULL;
+   void *sign = NULL;
+   void *data;
+   int head[3];
+   EVP_MD_CTX md_ctx;
+   struct stat st_buf;
+   Eet_Error err = EET_ERROR_NONE;
+   int sign_length;
+   int x509_length;
+   int fd;
+
+   /* A few check and flush pending write. */
+   if (!fp
+       || !key
+       || !key->certificate
+       || !key->private_key)
+     return EET_ERROR_BAD_OBJECT;
+
+   /* Get the file size. */
+   fd = fileno(fp);
+   if (fd < 0) return EET_ERROR_BAD_OBJECT;
+   if (fstat(fd, &st_buf) < 0) return EET_ERROR_MMAP_FAILED;
+
+   /* Map the file in memory. */
+   data = mmap(NULL, st_buf.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
+   if (data == MAP_FAILED) return EET_ERROR_MMAP_FAILED;
+
+   sign_length = EVP_PKEY_size(key->private_key);
+   sign = malloc(sign_length);
+   if (sign == NULL)
+     {
+       err = EET_ERROR_OUT_OF_MEMORY;
+       goto on_error;
+     }
+
+   /* Do the signature. */
+   EVP_SignInit(&md_ctx, EVP_sha1());
+   EVP_SignUpdate (&md_ctx, data, st_buf.st_size);
+   err = EVP_SignFinal (&md_ctx, sign, &sign_length, key->private_key);
+   if (err != 1)
+     {
+       ERR_print_errors_fp(stdout);
+       err = EET_ERROR_SIGNATURE_FAILED;
+       goto on_error;
+     }
+
+   /* Give me the der (binary form for X509). */
+   x509_length = i2d_X509(key->certificate, &x509_der);
+   if (x509_length < 0)
+     {
+       ERR_print_errors_fp(stdout);
+       err = EET_ERROR_X509_ENCODING_FAILED;
+       goto on_error;
+     }
+
+   /* Append the signature at the end of the file. */
+   head[0] = (int) htonl ((unsigned int) EET_MAGIC_SIGN);
+   head[1] = (int) htonl ((unsigned int) sign_length);
+   head[2] = (int) htonl ((unsigned int) x509_length);
+
+   if (fwrite(head, sizeof(head), 1, fp) != 1)
+     {
+       err = EET_ERROR_WRITE_ERROR;
+       goto on_error;
+     }
+   if (fwrite(sign, sign_length, 1, fp) != 1)
+     {
+       err = EET_ERROR_WRITE_ERROR;
+       goto on_error;
+     }
+   if (fwrite(x509_der, x509_length, 1, fp) != 1)
+     {
+       err = EET_ERROR_WRITE_ERROR;
+       goto on_error;
+     }
+
+ on_error:
+   if (x509_der) OPENSSL_free(x509_der);
+   if (sign != NULL) free(sign);
+   munmap(data, st_buf.st_size);
+   return err;
+#else
+   return EET_ERROR_NOT_IMPLEMENTED;
+#endif
+}
+
+const void*
+eet_identity_check(const void *data_base, unsigned int data_length,
+                  const void *signature_base, unsigned int signature_length,
+                  int *x509_length)
+{
+#ifdef HAVE_SIGNATURE
+   const int *header = signature_base;
+   const unsigned char *sign;
+   const unsigned char *cert_der;
+   const unsigned char *tmp;
+   EVP_PKEY *pkey;
+   X509 *x509;
+   EVP_MD_CTX md_ctx;
+   int sign_length;
+   int cert_length;
+   int magic;
+   int err;
+
+   if (signature_length < sizeof(int) * 3) return NULL;
+
+   magic = ntohl(header[0]);
+   sign_length = ntohl(header[1]);
+   cert_length = ntohl(header[2]);
+
+   if (magic != EET_MAGIC_SIGN) return NULL;
+   if (sign_length + cert_length + sizeof(int) * 3 > signature_length) return NULL;
+
+   sign = signature_base + sizeof(int) * 3;
+   cert_der = sign + sign_length;
+
+   /* Strange but d2i_X509 seems to put 0 all over the place. */
+   tmp = alloca(cert_length);
+   memcpy((char*) tmp, cert_der, cert_length);
+   x509 = d2i_X509(NULL, &tmp, cert_length);
+   if (x509 == NULL) return NULL;
+
+   /* Get public key - eay */
+   pkey=X509_get_pubkey(x509);
+   if (pkey == NULL)
+     {
+       X509_free(x509);
+       return NULL;
+     }
+
+   /* Verify the signature */
+   EVP_VerifyInit(&md_ctx, EVP_sha1());
+   EVP_VerifyUpdate(&md_ctx, data_base, data_length);
+   err = EVP_VerifyFinal(&md_ctx, sign, sign_length, pkey);
+
+   X509_free(x509);
+   EVP_PKEY_free(pkey);
+
+   if (err != 1)
+     return NULL;
+
+   if (x509_length) *x509_length = cert_length;
+   return cert_der;
+#else
+   return NULL;
+#endif
+}
+
+EAPI void
+eet_identity_certificate_print(const unsigned char *certificate, int der_length, FILE *out)
+{
+#ifdef HAVE_SIGNATURE
+   const unsigned char *tmp;
+   X509 *x509;
+
+   if (certificate == NULL
+       || out == NULL
+       || der_length <= 0)
+     {
+       fprintf(out, "No certificate provided.\n");
+       return ;
+     }
+
+   /* Strange but d2i_X509 seems to put 0 all over the place. */
+   tmp = alloca(der_length);
+   memcpy((char*) tmp, certificate, der_length);
+   x509 = d2i_X509(NULL, &tmp, der_length);
+   if (x509 == NULL)
+     {
+       fprintf(out, "Not a valid certificate.\n");
+       return ;
+     }
+
+   fprintf(out, "Public certificate :\n");
+   X509_print_fp(out, x509);
+
+   X509_free(x509);
+#else
+   fprintf(out, "You need to compile signature support in EET.\n");
+#endif
+}
+
+Eet_Error
+eet_cypher(void *data, unsigned int size, const char *key, unsigned int length)
+{
+#ifdef HAVE_CYPHER
+#else
+   return EET_ERROR_NOT_IMPLEMENTED;
+#endif
+}
+
+Eet_Error
+eet_decypher(void *data, unsigned int size, const char *key, unsigned int length)
+{
+#ifdef HAVE_CYPHER
+#else
+   return EET_ERROR_NOT_IMPLEMENTED;
+#endif
+}
index 9d0be1c..76e60df 100644 (file)
@@ -69,14 +69,17 @@ struct _Eet_File
    FILE                 *fp;
    FILE                        *readfp;
    Eet_File_Header      *header;
-   const unsigned char  *data;
    Eet_Dictionary       *ed;
+   Eet_Key             *key;
+   const unsigned char  *data;
+   const void           *x509_der;
 
    int                   magic;
    int                   references;
 
    Eet_File_Mode         mode;
    int                   data_size;
+   int                   x509_length;
    time_t                mtime;
 
    unsigned char         writes_pending : 1;
@@ -158,6 +161,11 @@ struct
 } dictionary[num_dictionary_entries];
 /* now start the string stream. */
 /* and right after them the data stream. */
+int magic_sign; /* Optional, only if the eet file is signed. */
+int signature_length; /* Signature length. */
+int x509_length; /* Public certificate that signed the file. */
+char signature[signature_length]; /* The signature. */
+char x509[x509_length]; /* The public certificate. */
 #endif
 
 #define EET_FILE2_HEADER_COUNT                  3
@@ -379,8 +387,11 @@ eet_flush2(Eet_File *ef)
      return EET_ERROR_NONE;
    if (ef->mode == EET_FILE_MODE_READ_WRITE && ef->fp == NULL)
      {
+       int fd;
+
        unlink(ef->path);
-       ef->fp = fopen(ef->path, "wb");
+       fd = open(file, O_CREAT | O_TRUNC | O_RDWR, S_IRUSR | S_IWUSR);
+       ef->fp = fdopen(fd, "wb");
        if (!ef->fp) return EET_ERROR_NOT_WRITABLE;
        fcntl(fileno(ef->fp), F_SETFD, FD_CLOEXEC);
      }
@@ -516,6 +527,17 @@ eet_flush2(Eet_File *ef)
           }
      }
 
+   /* flush all write to the file. */
+   fflush(ef->fp);
+
+   /* append signature if required */
+   if (ef->key)
+     {
+       error = eet_identity_sign(ef->fp, ef->key);
+       if (error != EET_ERROR_NONE)
+         goto sign_error;
+     }
+
    /* no more writes pending */
    ef->writes_pending = 0;
 
@@ -651,6 +673,7 @@ eet_flush(Eet_File *ef)
        ef->fp = NULL;
        return EET_ERROR_WRITE_ERROR;
      }
+   sign_error:
    fclose(ef->fp);
    ef->fp = NULL;
    return EET_ERROR_WRITE_ERROR;
@@ -660,6 +683,19 @@ eet_flush(Eet_File *ef)
 EAPI int
 eet_init(void)
 {
+#ifdef HAVE_OPENSSL
+   /* Just load the crypto library error strings,
+    * SSL_load_error_strings() loads the crypto AND the SSL ones */
+   /* SSL_load_error_strings();*/
+   static int call_once = 0;
+
+   if (call_once == 0)
+     {
+       call_once = 1;
+       ERR_load_crypto_strings();
+     }
+
+#endif
    return ++eet_initcount;
 }
 
@@ -738,6 +774,7 @@ eet_internal_read2(Eet_File *ef)
    int           bytes_directory_entries;
    int           num_dictionary_entries;
    int           bytes_dictionary_entries;
+   int           signature_base_offset;
    int           i;
 
    index += sizeof(int);
@@ -787,6 +824,8 @@ eet_internal_read2(Eet_File *ef)
    if (eet_test_close(!ef->header->directory->nodes, ef))
      return NULL;
 
+   signature_base_offset = 0;
+
    /* actually read the directory block - all of it, into ram */
    for (i = 0; i < num_directory_entries; ++i)
      {
@@ -850,6 +889,10 @@ eet_internal_read2(Eet_File *ef)
              if (efn->data)
                memcpy(efn->data, ef->data + efn->offset, efn->size);
           }
+
+       /* compute the possible position of a signature */
+       if (signature_base_offset < efn->offset + efn->size)
+         signature_base_offset = efn->offset + efn->size;
      }
 
    ef->ed = NULL;
@@ -906,9 +949,35 @@ eet_internal_read2(Eet_File *ef)
             ef->ed->all[j].hash = hash;
              if (ef->ed->all[j].prev == -1)
                ef->ed->hash[hash] = j;
+
+            /* compute the possible position of a signature */
+            if (signature_base_offset < offset + ef->ed->all[j].len)
+              signature_base_offset = offset + ef->ed->all[j].len;
           }
      }
 
+   /* Check if the file is signed */
+   ef->x509_der = NULL;
+   ef->x509_length = 0;
+   if (signature_base_offset < ef->data_size)
+     {
+#ifdef HAVE_SIGNATURE
+       const unsigned char *buffer = ((const unsigned char*) ef->data) + signature_base_offset;
+       ef->x509_der = eet_identity_check(ef->data, signature_base_offset,
+                                         buffer, ef->data_size - signature_base_offset,
+                                         &ef->x509_length);
+
+       if (ef->x509_der == NULL)
+         {
+            ef->delete_me_now = 1;
+            eet_close(ef);
+            return NULL;
+         }
+#else
+       fprintf(stderr, "This file could be signed but you didn't compile the necessary code to check the signature.\n");
+#endif
+     }
+
    return ef;
 }
 
@@ -1120,6 +1189,7 @@ eet_memopen_read(const void *data, size_t size)
 
    ef->ed = NULL;
    ef->path = NULL;
+   ef->key = NULL;
    ef->magic = EET_MAGIC_FILE;
    ef->references = 1;
    ef->mode = EET_FILE_MODE_READ;
@@ -1229,6 +1299,7 @@ eet_open(const char *file, Eet_File_Mode mode)
 
    /* fill some of the members */
    ef->fp = fp;
+   ef->key = NULL;
    ef->readfp = NULL;
    ef->path = ((char *)ef) + sizeof(Eet_File);
    memcpy(ef->path, file, file_len);
@@ -1297,6 +1368,32 @@ eet_mode_get(Eet_File *ef)
      return ef->mode;
 }
 
+EAPI const void *
+eet_identity_x509(Eet_File *ef, int *der_length)
+{
+   if (!ef->x509_der) return NULL;
+
+   if (der_length) *der_length = ef->x509_length;
+   return ef->x509_der;
+}
+
+EAPI Eet_Error
+eet_identity_set(Eet_File *ef, Eet_Key *key)
+{
+   Eet_Key *tmp = ef->key;
+
+   if (!ef) return EET_ERROR_BAD_OBJECT;
+
+   ef->key = key;
+   eet_identity_ref(ef->key);
+   eet_identity_unref(tmp);
+
+   /* flags that writes are pending */
+   ef->writes_pending = 1;
+
+   return EET_ERROR_NONE;
+}
+
 EAPI Eet_Error
 eet_close(Eet_File *ef)
 {
@@ -1312,6 +1409,9 @@ eet_close(Eet_File *ef)
    /* flush any writes */
    err = eet_flush2(ef);
 
+   eet_identity_unref(ef->key);
+   ef->key = NULL;
+
    /* if not urgent to delete it - dont free it - leave it in cache */
    if ((!ef->delete_me_now) && (ef->mode == EET_FILE_MODE_READ))
      return EET_ERROR_NONE;
diff --git a/src/tests/cert.pem b/src/tests/cert.pem
new file mode 100644 (file)
index 0000000..3265462
--- /dev/null
@@ -0,0 +1,22 @@
+-----BEGIN CERTIFICATE-----
+MIIDmTCCAwKgAwIBAgIJAIKWPcLUT5FAMA0GCSqGSIb3DQEBBQUAMIGQMQswCQYD
+VQQGEwJGUjEWMBQGA1UECBMNSWxlLURlLUZyYW5jZTEOMAwGA1UEBxMFUGFyaXMx
+FjAUBgNVBAoTDUVubGlnaHRlbm1lbnQxDjAMBgNVBAsTBVRlc3RzMQ0wCwYDVQQD
+EwRCQUlMMSIwIAYJKoZIhvcNAQkBFhNjZWRyaWMuYmFpbEBmcmVlLmZyMB4XDTA4
+MDczMDEzNDU1OVoXDTA5MDczMDEzNDU1OVowgZAxCzAJBgNVBAYTAkZSMRYwFAYD
+VQQIEw1JbGUtRGUtRnJhbmNlMQ4wDAYDVQQHEwVQYXJpczEWMBQGA1UEChMNRW5s
+aWdodGVubWVudDEOMAwGA1UECxMFVGVzdHMxDTALBgNVBAMTBEJBSUwxIjAgBgkq
+hkiG9w0BCQEWE2NlZHJpYy5iYWlsQGZyZWUuZnIwgZ8wDQYJKoZIhvcNAQEBBQAD
+gY0AMIGJAoGBAMiE486eROKePG0/639D4XTTDR9XSRWp1xqZzq7P+jjWRFbZ/MWV
+IVzkc4MRm83UOolbPj76LjM10cseaVAhK7G9CHp2dur4alWvdCXPH5Q+LPOFS9gM
+x0Jz9EZeHHOHZKLyJdKSmot+zluwJTLe081RRUwzNKct6JrVVG/7SmITAgMBAAGj
+gfgwgfUwHQYDVR0OBBYEFEFar6doT5ImL2rf0rJX7EYQqtYQMIHFBgNVHSMEgb0w
+gbqAFEFar6doT5ImL2rf0rJX7EYQqtYQoYGWpIGTMIGQMQswCQYDVQQGEwJGUjEW
+MBQGA1UECBMNSWxlLURlLUZyYW5jZTEOMAwGA1UEBxMFUGFyaXMxFjAUBgNVBAoT
+DUVubGlnaHRlbm1lbnQxDjAMBgNVBAsTBVRlc3RzMQ0wCwYDVQQDEwRCQUlMMSIw
+IAYJKoZIhvcNAQkBFhNjZWRyaWMuYmFpbEBmcmVlLmZyggkAgpY9wtRPkUAwDAYD
+VR0TBAUwAwEB/zANBgkqhkiG9w0BAQUFAAOBgQCpZJhk8BgQh0foiMkOwOMKvObq
+GxAzqjbr7iU9tEvJgwukCBv59ndBM0B5l5ybQdIYWQJOfZE1HTvB60swZMwqap9X
+5QXgewymuXiVk+roVh34wg8Pg8F588G2BtLIoujY/gN3WJQR7YPD34iTPc4koV+A
+4vs6nmL6wtW21+hsaw==
+-----END CERTIFICATE-----
index 003dfa5..c52b15c 100644 (file)
@@ -1,10 +1,17 @@
+#include <sys/types.h>
+#include <sys/stat.h>
 #include <stdlib.h>
 #include <string.h>
 #include <strings.h>
 #include <stdio.h>
+#include <fcntl.h>
 
 #include <check.h>
 
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
 #include "eet_suite.h"
 
 START_TEST(eet_test_init)
@@ -1146,6 +1153,77 @@ START_TEST(eet_small_image)
 }
 END_TEST
 
+START_TEST(eet_identity_simple)
+{
+   const char *buffer = "Here is a string of data to save !";
+   const void *tmp;
+   Eet_File *ef;
+   Eet_Key *k;
+   char *test;
+   char *file = strdup("/tmp/eet_suite_testXXXXXX");
+   int size;
+   int fd;
+
+   eet_init();
+
+   mktemp(file);
+   chdir("src/tests");
+
+   /* Sign an eet file. */
+   ef = eet_open(file, EET_FILE_MODE_WRITE);
+   fail_if(!ef);
+
+   fail_if(!eet_write(ef, "keys/tests", buffer, strlen(buffer) + 1, 0));
+
+   k = eet_identity_open("cert.pem", "key.pem", NULL);
+   fail_if(!k);
+
+   fail_if(eet_identity_set(ef, k) != EET_ERROR_NONE);
+
+   eet_close(ef);
+
+   /* Open a signed file. */
+   ef = eet_open(file, EET_FILE_MODE_READ);
+   fail_if(!ef);
+
+   test = eet_read(ef, "keys/tests", &size);
+   fail_if(!test);
+   fail_if(size != strlen(buffer) + 1);
+
+   fail_if(memcmp(test, buffer, strlen(buffer) + 1) != 0);
+
+   tmp = eet_identity_x509(ef, &size);
+   fail_if(tmp == NULL);
+
+   eet_close(ef);
+
+   /* As we are changing file contain in less than 1s, this could get unnoticed
+      by eet cache system. */
+   eet_clearcache();
+
+   /* Corrupting the file. */
+   fd = open(file, O_WRONLY);
+   fail_if(fd < 0);
+
+   fail_if(lseek(fd, 200, SEEK_SET) != 200);
+   fail_if(write(fd, "42", 2) != 2);
+   fail_if(lseek(fd, 50, SEEK_SET) != 50);
+   fail_if(write(fd, "42", 2) != 2);
+   fail_if(lseek(fd, 88, SEEK_SET) != 88);
+   fail_if(write(fd, "42", 2) != 2);
+
+   close(fd);
+
+   /* Attempt to open a modified file. */
+   ef = eet_open(file, EET_FILE_MODE_READ);
+   fail_if(ef);
+
+   fail_if(unlink(file) != 0);
+
+   eet_shutdown();
+}
+END_TEST
+
 Suite *
 eet_suite(void)
 {
@@ -1175,6 +1253,12 @@ eet_suite(void)
    tcase_add_test(tc, eet_small_image);
    suite_add_tcase(s, tc);
 
+#ifdef HAVE_SIGNATURE
+   tc = tcase_create("Eet Identity");
+   tcase_add_test(tc, eet_identity_simple);
+   suite_add_tcase(s, tc);
+#endif
+
    return s;
 }
 
diff --git a/src/tests/key.pem b/src/tests/key.pem
new file mode 100644 (file)
index 0000000..74763ca
--- /dev/null
@@ -0,0 +1,15 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIICXAIBAAKBgQDIhOPOnkTinjxtP+t/Q+F00w0fV0kVqdcamc6uz/o41kRW2fzF
+lSFc5HODEZvN1DqJWz4++i4zNdHLHmlQISuxvQh6dnbq+GpVr3Qlzx+UPizzhUvY
+DMdCc/RGXhxzh2Si8iXSkpqLfs5bsCUy3tPNUUVMMzSnLeia1VRv+0piEwIDAQAB
+AoGAfLLHyNJ8HEIzae16UmawaqplWrw5YxOABbbo5aXJAledoDVoEKexW8dmXngw
+4Eu/K3RmvVtwJ8CsexiqfX5jYMU+YKRbww6Vqr/punIUhiEHVarHMFKG9yo14qSa
+z2xPgXvC5p7/Rhci+rAUp36S5kIHch5sLhEEcJayymyzDyECQQD/5B3JdpcovrSI
++nyZ8Iub2+I3f3uox6m1DKxHead26ICoIr7VCnPV5J1gLIB2MofVCbKhmy4PNi5a
+0QdvazJfAkEAyJq9Y+9SQ4sCOVDrFklJxhXuZE4WbnR32XsBdnQ9dauo0E2vDVkv
+6mHnzMWroTjLv4hH5nufE5NvMo8PNGB0zQJAFOKkf737JmsyRv/Szamxa14t/4Ob
+LzJkqo9HPGo0feMKJS74zmCVBb8sDR50ubD0HzI0bzZAMyOj8uuepLxmFwJAH+RR
+5bhfeLN52AjgRvvBycckzjeH42mKwD2I/v794l43CV7ATLv4HSgRhQGMBqaT5dBR
+tffDU4Zl8EDEJwyKpQJBAJ2NNacURTyavU699QJOIdGAsA4KXici8H3PuuWMtHLR
+RKdPFeaCRn+9p7Tglf0rH9hUGOpUXHYD3+ECt6gnVDc=
+-----END RSA PRIVATE KEY-----