在unix中用于身份验证的crypt函数如下所述:
char *crypt(const char *key, const char *salt);
假设我有key
(长度 n )和salt
(长度 m ),那么什么时间调用此函数的复杂性(算法顺序)?
答案 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
方法来支持上述分析。结果如下。
绘制的是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