"Fossies" - the Fresh Open Source Software Archive

Member "cryptofs-0.6.0/src/cryptofs/crypto.c" (1 Jul 2006, 10437 Bytes) of package /linux/misc/old/cryptofs-0.6.0.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "crypto.c" see the Fossies "Dox" file reference documentation.

    1 /*
    2  * Copyright (C) 2003-2006 Christoph Hohmann
    3  *
    4  * This program is free software; you can redistribute it and/or modify
    5  * it under the terms of the GNU General Public License as published by
    6  * the Free Software Foundation; either version 2 of the License, or
    7  * (at your option) any later version.
    8  *
    9  * This program is distributed in the hope that it will be useful,
   10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
   11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   12  * GNU General Public License for more details.
   13  *
   14  * You should have received a copy of the GNU General Public License
   15  * along with this program; if not, write to the Free Software
   16  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
   17  */
   18 
   19 #include "config.h"
   20 
   21 #include <stdio.h>
   22 
   23 #include <sys/types.h>
   24 #include <unistd.h>
   25 #include <fcntl.h>
   26 
   27 #include <glib.h>
   28 #include <gcrypt.h>
   29 
   30 #include "cryptofs.h"
   31 #include "crypto.h"
   32 #include "base64.h"
   33 #include "utils.h"
   34 
   35 struct _CryptoCtxGlobal {
   36     int               count;
   37 
   38     int               cipher;
   39     gchar            *key;
   40     guint             keylen;
   41     guchar          **salts;
   42     unsigned int          blocksize;
   43     long int              fileblocksize;
   44     long int              num_of_salts;
   45 };
   46 
   47 struct _CryptoCtxLocal {
   48     CryptoCtxGlobal      *global;
   49 
   50     gcry_cipher_hd_t          cipher_hd;
   51     void             *filebuf;
   52 };
   53 
   54 static void generate_key(int cipher, int md, const gchar *pass, gchar **key, guint *keylen)
   55 {
   56     int i;
   57     int mdlen, buflen;
   58     gchar *keybuf;
   59 
   60     gcry_cipher_algo_info(cipher, GCRYCTL_GET_KEYLEN, NULL, keylen);
   61     mdlen = gcry_md_get_algo_dlen(md);
   62 
   63     buflen = mdlen < *keylen ? *keylen : mdlen;
   64     keybuf = g_new0(gchar, buflen);
   65     memset(keybuf, 0, buflen);
   66 
   67     gcry_md_hash_buffer(md, keybuf, pass, strlen(pass));
   68     if (mdlen < *keylen)
   69     for (i = mdlen; i < *keylen; i++)
   70         keybuf[i] = keybuf[i % mdlen];
   71 
   72     *key = keybuf;
   73 }
   74 
   75 static gcry_cipher_hd_t open_cipher(CryptoCtxGlobal *gctx, int cipher)
   76 {
   77     gcry_cipher_hd_t cipherhd = NULL;
   78 
   79     if (gcry_cipher_open(&cipherhd, cipher, GCRY_CIPHER_MODE_CFB, 0) != GPG_ERR_NO_ERROR)
   80     return NULL;
   81 
   82     if (gcry_cipher_setkey(cipherhd, gctx->key, gctx->keylen) != GPG_ERR_NO_ERROR) {
   83     gcry_cipher_close(cipherhd);
   84     cipherhd  = NULL;
   85     }
   86 
   87     return cipherhd;
   88 }
   89 
   90 CryptoCtxGlobal *crypto_create_global_ctx(const gchar *cipheralgo, const gchar *mdalgo, long int fileblocksize, long int num_of_salts, PasswordQuery *query)
   91 {
   92     int cipher, md, i;
   93     char *pass;
   94     guchar *salts;
   95     CryptoCtxGlobal *gctx;
   96     gcry_cipher_hd_t cipher_hd;
   97 
   98     gcry_check_version("1.1.44");
   99 
  100     cipher = gcry_cipher_map_name(cipheralgo);
  101     md = gcry_md_map_name(mdalgo);
  102 
  103     pass = query->getPassword();
  104     if (pass == NULL)
  105     return NULL;
  106 
  107     gctx = g_new0(CryptoCtxGlobal, 1);
  108     gctx->cipher = cipher;
  109     gctx->fileblocksize = fileblocksize;
  110     gctx->num_of_salts = num_of_salts;
  111 
  112     generate_key(gctx->cipher, md, pass, &gctx->key, &gctx->keylen);
  113     query->freePassword(pass);
  114 
  115     cipher_hd = open_cipher(gctx, gctx->cipher);
  116     gcry_cipher_algo_info(gctx->cipher, GCRYCTL_GET_BLKLEN, NULL, &gctx->blocksize);
  117     salts = g_malloc0(num_of_salts * gctx->blocksize);
  118     gcry_cipher_setiv(cipher_hd, salts, gctx->blocksize);
  119     gcry_cipher_encrypt(cipher_hd, salts, num_of_salts * gctx->blocksize, NULL, 0);
  120     gctx->salts = g_new0(guchar *, num_of_salts);
  121     for (i = 0; i < num_of_salts; i++)
  122         gctx->salts[i] = &salts[i * gctx->blocksize];
  123     gctx->count = 0;
  124     gcry_cipher_close(cipher_hd);
  125     cipher_hd = NULL;
  126 
  127     return gctx;
  128 }
  129 
  130 CryptoCtxGlobal *crypto_create_global_ctx_default(const gchar *cipheralgo, const gchar *mdalgo, long int fileblocksize, long int num_of_salts)
  131 {
  132     return crypto_create_global_ctx(cipheralgo, mdalgo, fileblocksize, num_of_salts, getDefaultPasswordQuery());
  133 }
  134 
  135 CryptoCtxLocal *crypto_create_local_ctx(CryptoCtxGlobal *gctx)
  136 {
  137     gcry_cipher_hd_t cipher_hd;
  138     CryptoCtxLocal *ctx;
  139 
  140     cipher_hd = open_cipher(gctx, gctx->cipher);
  141     if (cipher_hd == NULL) {
  142         printf("failed to initialize cipher\n");
  143         return NULL;
  144     }
  145 
  146     ctx = g_new0(CryptoCtxLocal, 1);
  147     ctx->global = gctx;
  148     ctx->cipher_hd = cipher_hd;
  149     ctx->filebuf = g_malloc0(gctx->fileblocksize);
  150 
  151     gctx->count++;
  152 
  153     return ctx;
  154 }
  155 
  156 void crypto_destroy_local_ctx(CryptoCtxLocal *ctx)
  157 {
  158     g_free(ctx->filebuf);
  159     gcry_cipher_close(ctx->cipher_hd);
  160     ctx->global->count--;
  161     if (ctx->global->count == 0) {
  162     g_free(ctx->global->salts[0]);
  163     g_free(ctx->global->salts);
  164     g_free(ctx->global->key);
  165     g_free(ctx->global);
  166     }
  167     g_free(ctx);
  168 }
  169 
  170 #define CONFIGFILE_REPLACEMENT ".!ryptofs"
  171 
  172 char *crypto_encrypt_name(CryptoCtxLocal *ctx, const char *name)
  173 {
  174     gchar *tmpname, *ret;
  175     int len;
  176     gboolean hidden = FALSE;
  177 
  178     g_return_val_if_fail(ctx != NULL, NULL);
  179     g_return_val_if_fail(name != NULL, NULL);
  180     g_return_val_if_fail(name[0] != '\0', NULL);
  181 
  182     if (!strcmp(name, ".") || !strcmp(name, ".."))
  183     return g_strdup(name);
  184 
  185     if (name[0] == '.')
  186     hidden = TRUE;
  187 
  188     tmpname = alloca(strlen(name) + 1);
  189     strcpy(tmpname, name + (hidden ? 1 : 0));
  190     gcry_cipher_setiv(ctx->cipher_hd, ctx->global->salts[0], ctx->global->blocksize);
  191     gcry_cipher_encrypt(ctx->cipher_hd, tmpname, strlen(name) - (hidden ? 1 : 0), NULL, 0);
  192 
  193     ret = g_new0(gchar, norm2baselen(strlen(name)) + 5);
  194     len = base64_encode(ret + (hidden ? 1 : 0), tmpname, strlen(name) - (hidden ? 1 : 0));
  195 
  196     if (hidden)
  197     ret[0] = '.';
  198 
  199     *(ret + len + (hidden ? 1 : 0)) = '\0';
  200 
  201     if (strcmp(ret, CONFIGFILE) == 0) {
  202     g_free(ret);
  203     ret = g_strdup(CONFIGFILE_REPLACEMENT);
  204     }
  205 
  206     return ret;
  207 }
  208 
  209 char *crypto_decrypt_name(CryptoCtxLocal *ctx, const char *name)
  210 {
  211     char *tmpname, *ret;
  212     int len;
  213     gboolean hidden = FALSE;
  214 
  215     g_return_val_if_fail(ctx != NULL, NULL);
  216     g_return_val_if_fail(name != NULL, NULL);
  217     g_return_val_if_fail(name[0] != '\0', NULL);
  218 
  219     if (!strcmp(name, ".") || !strcmp(name, ".."))
  220     return g_strdup(name);
  221 
  222     if (strcmp(name, CONFIGFILE_REPLACEMENT) == 0)
  223     name = CONFIGFILE;
  224 
  225     if (name[0] == '.')
  226     hidden = TRUE;
  227 
  228     tmpname = alloca(base2normlen(strlen(name)) + 5);
  229     len = base64_decode(tmpname, name + (hidden ? 1 : 0), strlen(name) - (hidden ? 1 : 0));
  230 
  231     ret = g_new0(char, len + 1 + (hidden ? 1 : 0));
  232     memmove(ret + (hidden ? 1 : 0), tmpname, len);
  233     gcry_cipher_setiv(ctx->cipher_hd, ctx->global->salts[0], ctx->global->blocksize);
  234     gcry_cipher_decrypt(ctx->cipher_hd, ret + (hidden ? 1 : 0), len, NULL, 0);
  235 
  236     if (hidden)
  237     ret[0] = '.';
  238 
  239     return ret;
  240 }
  241 
  242 char *crypto_translate_path(CryptoCtxLocal *ctx, const char *path)
  243 {
  244     GString *ret;
  245     gchar *retstr;
  246     gchar **names, **cur;
  247 
  248     ret = g_string_new("");
  249 
  250     names = g_strsplit(path, "/", -1);
  251     for (cur = names; *cur != NULL; cur++) {
  252     gchar *encname;
  253 
  254     if (*cur[0] == '\0')
  255         continue;
  256 
  257     encname = crypto_encrypt_name(ctx, *cur);
  258     if (encname == NULL)
  259         continue;
  260     if (cur != names)
  261         g_string_append(ret, "/");
  262     g_string_append(ret, encname);
  263     g_free(encname);
  264     }
  265     g_strfreev(names);
  266 
  267     retstr = ret->str;
  268     g_string_free(ret, FALSE);
  269 
  270     return retstr;
  271 }
  272 
  273 int crypto_get_blocksize(CryptoCtxLocal *ctx)
  274 {
  275     return ctx->global->fileblocksize;
  276 }
  277 
  278 void *crypto_get_filebuf(CryptoCtxLocal *ctx)
  279 {
  280     return ctx->filebuf;
  281 }
  282 
  283 int crypto_readblock(CryptoCtxLocal *ctx, int fp, int block)
  284 {
  285     int res;
  286 
  287     if (lseek(fp, block * ctx->global->fileblocksize, SEEK_SET) < 0)
  288     return -1;
  289 
  290     if ((res = read(fp, ctx->filebuf, ctx->global->fileblocksize)) < 0)
  291     return -1;
  292 
  293     gcry_cipher_setiv(ctx->cipher_hd, ctx->global->salts[block % ctx->global->num_of_salts], ctx->global->blocksize);
  294     gcry_cipher_decrypt(ctx->cipher_hd, ctx->filebuf, res, NULL, 0);
  295 
  296     return res;
  297 }
  298 
  299 int crypto_writeblock(CryptoCtxLocal *ctx, int fp, int block, unsigned long size)
  300 {
  301     gcry_cipher_setiv(ctx->cipher_hd, ctx->global->salts[block % ctx->global->num_of_salts], ctx->global->blocksize);
  302     gcry_cipher_encrypt(ctx->cipher_hd, ctx->filebuf, size, NULL, 0);
  303 
  304     if (lseek(fp, block * ctx->global->fileblocksize, SEEK_SET) < 0)
  305     return -1;
  306 
  307     return write(fp, ctx->filebuf, size);
  308 }
  309 
  310 static void translate_pos(long long offset, unsigned long count,
  311               long long block, unsigned long blocksize,
  312               unsigned long *inblock_offset, unsigned long *inblock_count)
  313 {
  314     *inblock_offset = 0;
  315     *inblock_count = 0;
  316 
  317     if (block * blocksize < offset)
  318         *inblock_offset = offset % blocksize;
  319 
  320     if ((block + 1) * blocksize <= (offset + count))
  321         *inblock_count = blocksize - *inblock_offset;
  322     else
  323         *inblock_count = (offset + count) % blocksize - *inblock_offset;
  324 }
  325 
  326 int crypto_read(CryptoCtxLocal *ctx, int fp, void *buf, unsigned long count, long long offset)
  327 {
  328     long long block;
  329     unsigned long mempos = 0;
  330     unsigned long blocksize = ctx->global->fileblocksize;
  331     gboolean error = FALSE;
  332 
  333     block = offset / blocksize;
  334 
  335     for (block = offset / blocksize; block * blocksize < offset + count; block++) {
  336     unsigned long inblock_offset = 0;
  337     unsigned long inblock_count = 0;
  338     unsigned long inblock_read = 0;
  339     long res = 0;
  340 
  341     translate_pos(offset, count, block, blocksize, &inblock_offset, &inblock_count);
  342 
  343     if ((res = crypto_readblock(ctx, fp, block)) < 0) {
  344         error = TRUE;
  345         break;
  346     }
  347     inblock_read = res - inblock_offset;
  348 
  349     memmove(buf + mempos, ctx->filebuf + inblock_offset, inblock_read);
  350 
  351     mempos += inblock_read;
  352     if (inblock_read < inblock_count)
  353         break;
  354     }
  355 
  356     return error ? -1 : mempos;
  357 }
  358 
  359 int crypto_write(CryptoCtxLocal *ctx, int fp, void *buf, unsigned long count, long long offset)
  360 {
  361     long long block;
  362     unsigned long mempos = 0;
  363     unsigned long blocksize = ctx->global->fileblocksize;
  364     gboolean error = FALSE;
  365 
  366     block = offset / blocksize;
  367 
  368     for (block = offset / blocksize; block * blocksize < offset + count; block++) {
  369     unsigned long inblock_offset = 0;
  370     unsigned long inblock_count = 0;
  371 
  372     translate_pos(offset, count, block, blocksize, &inblock_offset, &inblock_count);
  373 
  374     if ((inblock_offset != 0) && (inblock_count != blocksize)) {
  375         if (crypto_readblock(ctx, fp, block) < 0) {
  376         error = TRUE;
  377         break;
  378         }
  379     }
  380 
  381     memmove(ctx->filebuf + inblock_offset, buf + mempos, inblock_count);
  382 
  383     if (crypto_writeblock(ctx, fp, block, inblock_offset + inblock_count) < 0) {
  384         error = TRUE;
  385         break;
  386     }
  387 
  388     mempos += inblock_count;
  389     }
  390 
  391     return error ? -1 : mempos;
  392 }