]> git.ipfire.org Git - thirdparty/bacula.git/commitdiff
New Block crypto functions and types for Volume encryption
authorAlain Spineux <alain@baculasystems.com>
Thu, 5 May 2022 08:49:00 +0000 (10:49 +0200)
committerEric Bollengier <eric@baculasystems.com>
Thu, 14 Sep 2023 11:56:59 +0000 (13:56 +0200)
- encapsulate openssl API
- include a unittest to show how to use and test it

bacula/src/lib/crypto.c
bacula/src/lib/crypto.h
bacula/src/lib/protos.h
bacula/src/stored/Makefile.in
bacula/src/stored/dev.h
bacula/src/tools/Makefile.in

index d6c64b3ef28175282d9fd7b78111599236da5606..fd60564a0715211319b56c24f4a678859251e4fb 100644 (file)
@@ -1714,3 +1714,155 @@ const char *crypto_strerror(crypto_error_t error) {
       return _("Unknown error");
    }
 }
+
+struct block_cipher_context
+{
+   int type;
+   int key_length;
+   int iv_length;
+   EVP_CIPHER_CTX *ctx;
+   const EVP_CIPHER *cipher;
+   unsigned char *key;
+   unsigned char *iv;
+};
+
+/* A cheap openssl handler */
+static void reportOpenSSLErrors()
+{
+   char buf[256];
+   unsigned long e = ERR_peek_error();
+   Dmsg2(1, "Block cipher error: Openssl ERROR %lu %s\n", e, ERR_error_string(e, buf));
+}
+
+BLOCK_CIPHER_CONTEXT *block_cipher_context_new(block_cipher_type blk_type)
+{
+   BLOCK_CIPHER_CONTEXT *blk_ctx;
+
+   if (blk_type<=BLOCK_CIPHER_NONE || BLOCK_CIPHER_LAST<=blk_type) {
+      Dmsg1(1, "Block cipher error: invalid cipher %d\n", blk_type);
+      return NULL;
+   }
+
+   EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new();
+   if (ctx == NULL) {
+      reportOpenSSLErrors();
+      return NULL;
+   }
+
+   blk_ctx = (BLOCK_CIPHER_CONTEXT *)malloc(sizeof(BLOCK_CIPHER_CONTEXT));
+   memset(blk_ctx, '\0', sizeof(BLOCK_CIPHER_CONTEXT));
+   blk_ctx->type = blk_type;
+   blk_ctx->ctx = ctx;
+   EVP_CIPHER_CTX_set_padding(blk_ctx->ctx, 1); // already the openssl default
+   switch (blk_type) {
+   case BLOCK_CIPHER_NULL:
+      blk_ctx->cipher = NULL;
+      blk_ctx->key_length = 16;
+      blk_ctx->iv_length = 16; // ATTN min size block_cipher_init_iv_header()
+      break;
+   case BLOCK_CIPHER_AES_128_XTS:
+      blk_ctx->cipher = EVP_aes_128_xts();
+      blk_ctx->key_length = EVP_CIPHER_key_length(blk_ctx->cipher);  // 32 bytes
+      blk_ctx->iv_length = EVP_CIPHER_iv_length(blk_ctx->cipher);    // 16 bytes
+      break;
+   case BLOCK_CIPHER_AES_256_XTS:
+      blk_ctx->cipher = EVP_aes_256_xts();
+      blk_ctx->key_length = EVP_CIPHER_key_length(blk_ctx->cipher); // 64 bytes
+      blk_ctx->iv_length = EVP_CIPHER_iv_length(blk_ctx->cipher);   // 16 bytes
+      break;
+   default:
+      ASSERT2(0, "unknown block cipher");
+   }
+   ASSERT(blk_ctx->iv_length >= 12); // (BlockNum, VolSessionId, VolSessionTime)
+   blk_ctx->key = (unsigned char*)malloc(blk_ctx->key_length);
+   blk_ctx->iv = (unsigned char*)malloc(blk_ctx->iv_length);
+   return blk_ctx;
+}
+
+void block_cipher_context_free(BLOCK_CIPHER_CONTEXT *blk_ctx)
+{
+   EVP_CIPHER_CTX_free(blk_ctx->ctx);
+   free(blk_ctx->key);
+   free(blk_ctx->iv);
+   free(blk_ctx);
+   return;
+}
+
+void block_cipher_init_key(BLOCK_CIPHER_CONTEXT *blk_ctx, const unsigned char *key)
+{
+   memcpy(blk_ctx->key, key, blk_ctx->key_length);
+}
+
+void block_cipher_init_iv(BLOCK_CIPHER_CONTEXT *blk_ctx, const unsigned char *iv)
+{
+   memcpy(blk_ctx->iv, iv, blk_ctx->iv_length);
+}
+
+void block_cipher_init_iv_header(BLOCK_CIPHER_CONTEXT *blk_ctx, uint32_t BlockNumber, uint32_t VolSessionId, uint32_t VolSessionTime)
+{
+   ser_declare;
+   ser_begin(blk_ctx->iv, 3*sizeof(uint32_t));
+   ser_uint32(BlockNumber);
+   ser_uint32(VolSessionId);
+   ser_uint32(VolSessionTime);
+   memset(blk_ctx->iv+3*sizeof(uint32_t), '\0', blk_ctx->iv_length-3*sizeof(uint32_t));
+}
+
+int block_cipher_encrypt(BLOCK_CIPHER_CONTEXT *blk_ctx, int len, const char *src, char *dst)
+{
+   int ret;
+   if (blk_ctx->type == BLOCK_CIPHER_NULL) {
+      memcpy(dst, src, len);
+      return 0;
+   }
+   ret = EVP_EncryptInit_ex(blk_ctx->ctx, blk_ctx->cipher, NULL, blk_ctx->key, blk_ctx->iv);
+   if (ret != 1) {
+      reportOpenSSLErrors();
+      return -1;
+   }
+   int outl1, outl2;
+   ret = EVP_EncryptUpdate(blk_ctx->ctx, (unsigned char *)dst, &outl1, (const unsigned char *)src, len);
+   if (ret != 1) {
+      reportOpenSSLErrors();
+      return -1;
+   }
+   ret = EVP_EncryptFinal_ex(blk_ctx->ctx, (unsigned char *)dst + outl1, &outl2);
+   if (ret != 1) {
+      reportOpenSSLErrors();
+      return -1;
+   }
+   ASSERTD(outl1+outl2==len, "unexpected encryption length");
+   return 0;
+}
+
+int block_cipher_decrypt(BLOCK_CIPHER_CONTEXT *blk_ctx, int len, const char *src, char *dst)
+{
+   int ret;
+   if (blk_ctx->type == BLOCK_CIPHER_NULL) {
+      memcpy(dst, src, len);
+      return 0;
+   }
+   ret = EVP_DecryptInit_ex(blk_ctx->ctx, blk_ctx->cipher, NULL, blk_ctx->key, blk_ctx->iv);
+   if (ret != 1) {
+      reportOpenSSLErrors();
+      return -1;
+   }
+   int outl1, outl2;
+   ret = EVP_DecryptUpdate(blk_ctx->ctx, (unsigned char *)dst, &outl1, (const unsigned char *)src, len);
+   if (ret != 1) {
+      reportOpenSSLErrors();
+      return -1;
+   }
+   ret = EVP_DecryptFinal_ex(blk_ctx->ctx, (unsigned char *)dst + outl1, &outl2);
+   if (ret != 1) {
+      reportOpenSSLErrors();
+      return -1;
+   }
+   ASSERTD(outl1+outl2==len, "unexpected decryption length");
+   return 0;
+}
+
+int block_cipher_get_key_length(BLOCK_CIPHER_CONTEXT *blk_ctx)
+{
+   return blk_ctx->key_length;
+}
index efac78fd915cc29e167463ba23d192ffe8271b33..04b5b606e3a395facf169bfd95d07ec5de13c54c 100644 (file)
@@ -108,6 +108,20 @@ typedef enum {
 #define CRYPTO_DIGEST_XXH3_64_SIZE   8 /*  64bits */
 #define CRYPTO_DIGEST_XXH3_128_SIZE 16 /* 128bits */
 
+typedef enum
+{
+   BLOCK_CIPHER_NONE        = 0,
+   BLOCK_CIPHER_NULL,
+   BLOCK_CIPHER_AES_128_XTS,
+   BLOCK_CIPHER_AES_256_XTS,
+   BLOCK_CIPHER_LAST
+} block_cipher_type;
+
+#define MAX_BLOCK_CIPHER_KEY_LEN      64 /* for BLOCK_CIPHER_AES_256_XTS */
+#define MAX_MASTERKEY_ID_LEN          20 /* this is the 40 hexa fingerprint */
+
+typedef struct block_cipher_context BLOCK_CIPHER_CONTEXT;
+
 /* Maximum Message Digest Size */
 #ifdef HAVE_OPENSSL
 
index e704078513b920cc32bbf2af7e298bf5a8a719d7..c206dc5793d46d1403418c9553f8e21ce2224510 100644 (file)
@@ -229,6 +229,15 @@ const char *       crypto_digest_name          (DIGEST *digest);
 crypto_digest_t    crypto_digest_stream_type   (int stream);
 const char *       crypto_strerror             (crypto_error_t error);
 
+BLOCK_CIPHER_CONTEXT *block_cipher_context_new(block_cipher_type blk_type);
+void block_cipher_context_free(BLOCK_CIPHER_CONTEXT *blk_ctx);
+void block_cipher_init_key(BLOCK_CIPHER_CONTEXT *blk_ctx, const unsigned char *key);
+void block_cipher_init_iv(BLOCK_CIPHER_CONTEXT *blk_ctx, const unsigned char *iv);
+void block_cipher_init_iv_header(BLOCK_CIPHER_CONTEXT *blk_ctx, uint32_t BlockNumber, uint32_t VolSessionId, uint32_t VolSessionTime);
+int block_cipher_encrypt(BLOCK_CIPHER_CONTEXT *blk_ctx, int len, const char *src, char *dst);
+int block_cipher_decrypt(BLOCK_CIPHER_CONTEXT *blk_ctx, int len, const char *src, char *dst);
+int block_cipher_get_key_length(BLOCK_CIPHER_CONTEXT *blk_ctx);
+
 /* daemon.c */
 void     daemon_start            ();
 
index 1a6ce2341afebd461eee924e69ac2b76d57d6848..aa08da4a86afb623f68a1e988a9e850c0e1399a6 100644 (file)
@@ -175,6 +175,9 @@ all: Makefile libbacsd.la drivers bacula-sd @STATIC_SD@ \
        @echo "===== Make of stored is good ===="
        @echo " "
 
+../lib/unittests.o: ../lib/unittests.c
+       (cd ../lib ; make unittests.o)
+
 bacula-sd: Makefile libbacsd.la $(SDOBJS) \
        ../lib/libbaccfg$(DEFAULT_ARCHIVE_TYPE) \
        ../lib/libbac$(DEFAULT_ARCHIVE_TYPE) \
@@ -278,7 +281,6 @@ btape: Makefile  $(TAPEOBJS) libbacsd.la drivers ../lib/libbac$(DEFAULT_ARCHIVE_
        $(LIBTOOL_LINK) $(CXX) $(TTOOL_LDFLAGS) $(LDFLAGS) -L../lib -L../findlib -o $@ $(TAPEOBJS) \
          $(SD_LIBS) $(DLIB) -lm $(LIBS) $(GETTEXT_LIBS) $(OPENSSL_LIBS)
 
-
 cloud_test.o: cloud_test.c
        @echo "Compiling $<"
        $(NO_ECHO)$(CXX) $(DEFS) $(DEBUG) -c $(CPPFLAGS) -I$(srcdir) \
index 2a0781ee8169a3fd1ed97fbaa66282a926306054..2b618dab19b517c4659e0b4eb0d2745e3455c2a1 100644 (file)
@@ -381,6 +381,8 @@ public:
    devstatmetrics_t devstatmetrics;    /* these are a device metrics for every device */
    bstatcollect *devstatcollector;        /* a pointer to daemon's statcollector */
 
+   BLOCK_CIPHER_CONTEXT *crypto_device_ctx;
+
    /* Methods */
    btime_t get_timer_count(); /* return the last timer interval (ms) */
 
index 71e5f135bbb28a0900adaa27e6a9f85080880ab9..fcedaff19a608b4a6e578aa3aa08afd7832e761e 100644 (file)
@@ -131,6 +131,11 @@ bnet_test: Makefile bnet_test.o ../lib/libbac$(DEFAULT_ARCHIVE_TYPE) ../findlib/
        $(LIBTOOL_LINK) $(CXX) $(LDFLAGS) -L../lib -L../findlib -o $@ bnet_test.o -lbacfind -lbac -lm \
          $(DLIB) $(LIBS) $(GETTEXT_LIBS) $(OPENSSL_LIBS)
 
+block_crypto_test: Makefile block_crypto_test.o ../lib/libbac$(DEFAULT_ARCHIVE_TYPE) ../lib/unittests.o
+       $(LIBTOOL_LINK) $(CXX) $(LDFLAGS) -L../lib -o $@ block_crypto_test.o ../lib/unittests.o -lbac -lm \
+         $(DLIB) $(LIBS) $(GETTEXT_LIBS) $(OPENSSL_LIBS)
+       $(LIBTOOL_INSTALL) $(INSTALL_PROGRAM) $@ $(DESTDIR)$(sbindir)/
+
 tags_test: Makefile test_tags.o ../lib/libbac$(DEFAULT_ARCHIVE_TYPE) ../lib/unittests.o
        $(LIBTOOL_LINK) $(CXX) $(LDFLAGS) -L../lib -o $@ test_tags.o ../lib/unittests.o -lbac \
          $(DLIB) $(LIBS)