Linux中crypt函数的时间复杂度是多少?

时间:2015-10-02 14:37:34

标签: c linux algorithm crypt

在unix中用于身份验证的crypt函数如下所述:

char *crypt(const char *key, const char *salt);

假设我有key(长度 n )和salt(长度 m ),那么什么时间调用此函数的复杂性(算法顺序)?

1 个答案:

答案 0 :(得分:3)

来自crypt的手册页:

  

salt 是从集合[a-zA-Z0-9./]中选择的双字符字符串。该字符串用于以4096种不同方式之一扰动算法。

  

通过获取的前八个字符中每个字符的最低7位,可以获得一个56位密钥。

然后使用如此获得的密钥来加密常量字符串(使用调整的DES算法),这需要恒定的时间。因此,该函数具有任何有效参数的常量运行时。请注意,此截断会导致密码非常弱。

melpomene所述,某些实现提供了crypt函数的扩展,允许选择更安全的模式。对于以下内容,我假设您正在使用GNU C库中的crypt函数。 The manual说:

  

对于基于MD5的算法, salt 应包含字符串$1$,后跟最多8个字符,由另一个$或结束字符串。 crypt的结果为盐,如果盐没有以{1}结尾,则后跟$,后跟字母./0-9A-Za-z中的22个字符,总共最多34个字符。 中的每个字符都很重要。

由于 salt 的长度由常量固定,并且加密散列函数的时间复杂度与输入的长度呈线性关系,因此crypt函数的总时间复杂度将在中是线性的。

除了{{3>之外,我的glibc版本还支持更安全的SHA-256(通过$5$选择)和SHA-512(通过$6$选择)加密哈希函数}}。它们的输入长度也具有线性时间复杂度。

由于我无法理解我现在应该做的任务,我已经定时了各种crypt方法来支持上述分析。结果如下。

MD5

绘制的是crypt函数在key字符串长度上花费的执行时间。每个数据系列都覆盖有线性回归,但DES除外,其中绘制了平均值。我很惊讶SHA-512实际上比SHA-256 更快

用于基准测试的代码在这里(benchmark.c)。

#define _GNU_SOURCE  /* crypt */

#include <errno.h>   /* errno, strerror */
#include <stdio.h>   /* FILE, fopen, fclose, fprintf */
#include <stdlib.h>  /* EXIT_{SUCCESS,FAILURE}, malloc, free, [s]rand */
#include <string.h>  /* size_t, strlen */
#include <assert.h>  /* assert */
#include <time.h>    /* CLOCKS_PER_SEC, clock_t, clock */
#include <unistd.h>  /* crypt */

/* Barrier to stop the compiler from re-ordering instructions. */
#define COMPILER_BARRIER asm volatile("" ::: "memory")

/* First character in the printable ASCII range. */
static const char ascii_first = ' ';

/* Last character in the printable ASCII range. */
static const char ascii_last = '~';

/*
  Benchmark the time it takes to crypt(3) a key of length *keylen* with salt
  *salt*.  The result is written to the stream *ostr* so its computation cannot
  be optimized away.
*/
static clock_t
measure_crypt(const size_t keylen, const char *const salt, FILE *const ostr)
{
  char * key;
  const char * passwd;
  clock_t t1;
  clock_t t2;
  size_t i;
  key = malloc(keylen + 1);
  if (key == NULL)
    return ((clock_t) -1);
  /*
    Generate a random key.  The randomness is extremely poor; never do this in
    cryptographic applications!
  */
  for (i = 0; i < keylen; ++i)
    key[i] = ascii_first + rand() % (ascii_last - ascii_first);
  key[keylen] = '\0';
  assert(strlen(key) == keylen);
  COMPILER_BARRIER;
  t1 = clock();
  COMPILER_BARRIER;
  passwd = crypt(key, salt);
  COMPILER_BARRIER;
  t2 = clock();
  COMPILER_BARRIER;
  fprintf(ostr, "%s\n", passwd);
  free(key);
  return t2 - t1;
}

/*
  The program can be called with zero or one arguments.  The argument, if
  given, will be used as salt.
*/
int
main(const int argc, const char *const *const argv)
{
  const size_t keymax = 2000;
  const size_t keystep = 100;
  const char * salt = "..";  /* default salt */
  FILE * devnull = NULL;  /* redirect noise to black hole */
  int status = EXIT_SUCCESS;
  size_t keylen;
  if (argc > 1)
    salt = argv[1];
  devnull = fopen("/dev/null", "r");
  if (devnull == NULL)
    goto label_catch;
  srand((unsigned) clock());
  for (keylen = 0; keylen <= keymax; keylen += keystep)
    {
      clock_t ticks;
      double millis;
      ticks= measure_crypt(keylen, salt, devnull);
      if (ticks < 0)
        goto label_catch;
      millis = 1.0E3 * ticks / CLOCKS_PER_SEC;
      fprintf(stdout, "%16zu %e\n", keylen, millis);
    }
  goto label_finally;
 label_catch:
  status = EXIT_FAILURE;
  fprintf(stderr, "error: %s\n", strerror(errno));
 label_finally:
  if (devnull != NULL)
    fclose(devnull);
  return status;
}

用于回归和绘图的Gnuplot脚本在这里(plot.gplt)。

set terminal 'svg'
set output 'timings.svg'

set xrange [0 : *]
set yrange [0 : *]

set key top left

set title 'crypt(3) benchmarks'
set xlabel 'key length / bytes'
set ylabel 'computation time / milliseconds'

des(x) = a_des
md5(x) = a_md5 + b_md5 * x
sha256(x) = a_sha256 + b_sha256 * x
sha512(x) = a_sha512 + b_sha512 * x

fit des(x) 'timings.des' via a_des
fit md5(x) 'timings.md5' via a_md5, b_md5
fit sha256(x) 'timings.sha256' via a_sha256, b_sha256
fit sha512(x) 'timings.sha512' via a_sha512, b_sha512

plot des(x)           w l notitle     lc '#75507b' lt 1 lw 2.5,     \
     'timings.des'    w p t 'DES'     lc '#5c3566' pt 7 ps 0.8,     \
     md5(x)           w l notitle     lc '#cc0000' lt 1 lw 2.5,     \
     'timings.md5'    w p t 'MD5'     lc '#a40000' pt 7 ps 0.8,     \
     sha256(x)        w l notitle     lc '#73d216' lt 1 lw 2.5,     \
     'timings.sha256' w p t 'SHA-256' lc '#4e9a06' pt 7 ps 0.8,     \
     sha512(x)        w l notitle     lc '#3465a4' lt 1 lw 2.5,     \
     'timings.sha512' w p t 'SHA-512' lc '#204a87' pt 7 ps 0.8

最后,Makefile用于将所有内容挂钩(GNUmakefile)。

CC := gcc
CPPFLAGS :=
CFLAGS := -Wall -O2
LDFLAGS :=
LIBS := -lcrypt

all: benchmark timings.svg timings.png

benchmark: benchmark.o
    ${CC} -o $@ ${CFLAGS} $^ ${LDFLAGS} ${LIBS}

benchmark.o: benchmark.c
    ${CC} -c ${CPPFLAGS} ${CFLAGS} $<

timings.svg: plot.gplt timings.des timings.md5 timings.sha256 timings.sha512
    gnuplot $<

timings.png: timings.svg
    convert $< $@

timings.des: benchmark
    ./$< '$(shell pwgen -ncs 2)' > $@

timings.md5: benchmark
    ./$< '$$1$$$(shell pwgen -ncs 8)' > $@

timings.sha256: benchmark
    ./$< '$$5$$$(shell pwgen -ncs 16)' > $@

timings.sha512: benchmark
    ./$< '$$6$$$(shell pwgen -ncs 16)' > $@

clean:
    rm -f benchmark benchmark.o fit.log $(wildcard *.o timings.*)

.PHONY: all clean