不熟练的密码散列[关闭]


33

本着“ 不熟练C竞赛”的精神,我正在发起不熟练C竞赛。该竞赛的目的是直接实现一些代码,同时在其中隐藏一个邪恶的bug。

比赛

您是在美国间谍机构的IT部门工作的秘密俄罗斯痣。您的美国老板刚刚要求您实施密码哈希算法,以用于加密秘密消息。

您的老板希望您实现以下功能:

f: String -> byte[16]

它将密码转换为16字节的数量,适合用作AES密钥。您的老板想要一个安全的功能,在这种情况下,这意味着不同的密码字符串应以压倒性的可能性生成不同的结果。例如,返回输入的md5哈希将是的简单实现f

当然,您在俄罗斯间谍机构中的真正上司会希望您颠覆这一过程。您的任务是实现f使俄罗斯人可以解密使用所返回的密钥加密的所有秘密消息f

为此,必须实现f以使其仅返回2 ^ 128个可能输出的一小部分。特别是,您f必须最多返回2 ^ 16个不同的结果,以便俄罗斯人可以轻松地用蛮力搜索想要解密的每条加密消息的正确密钥。

但是请记住,间谍活动会判处死刑。为了不被发现,您的函数f必须至少产生2 ^ 8个不同的结果,以便对的几个输出进行粗略的检查f就不可能发现重复的结果。 最重要的是,您引入的限制范围的代码f必须看起来是无意的,而不是故意的。 如果您曾经被拖入法庭,那么对于您是故意还是无意中引入了该错误,一定存在一些合理的疑问。

评判

我和我招募的其他两个人将对参赛作品进行评判(如果您想评判,请给我发电子邮件)。我将为获奖作品提供200点声望奖励。提交的内容必须在5月1日之前上传。

评审将考虑以下标准:

  • 是否f坚持以规范,即,它不会产生2和2之间^ 8 ^ 16个可能的输出。不要以为这是硬性限制,但是如果您超出范围,我们会扣除分数。
  • 该错误貌似是非故意错误的结果吗?
  • f外观的输出是否随机?
  • 您实施的时间越短f越好。
  • 您实施的f越清晰,效果越好。

笔记

您可以使用任何语言来实现您的代码。您正试图将错误隐藏起来,因此不建议使用混淆的代码。

您可能想看看以前的一些欠人手C比赛获胜者,以了解提交优秀作品的原因。

输入字符串将是可打印的ascii(包括32至126)。如果需要,可以假定一个合理的最大长度。


1
输入字符串有什么限制吗?好像只包含字母?
Ali1S232 2012年

@Gajet:您必须处理所有可打印的ASCII字符(32至126,包括32和126)。
基思·兰德尔

输出范围是全部16字节的字符串,还是仅可打印的字符串?
展位,2012年

@boothby:所有可能的16字节值(2 ^ 128种可能性)
Keith Randall

1
我投票结束这个问题是不合时宜的,因为在这个网站上,不熟练的挑战已不再是正题。meta.codegolf.stackexchange.com/a/8326/20469

Answers:


15

C

2 ^ 16个可能的输出(或使用的字符数的2 ^ 8倍)。
使用Linux的MD5实现(很好,AFAIK)。但这给出了相同的哈希值,例如,“ 40”和“ 42”。
编辑:重命名bcopymemcpy(当然交换参数)。
编辑:从程序转换为函数,可以更好地满足要求。

#include <string.h>
#include <openssl/md5.h>

void f(const unsigned char *input, unsigned char output[16]) {

    /* Put the input in a 32-byte buffer, padded with zeros. */
    unsigned char workbuf[32] = {0};
    strncpy(workbuf, input, sizeof(workbuf));

    unsigned char res[MD5_DIGEST_LENGTH];
    MD5(workbuf, sizeof(workbuf), res);

    /* NOTE: MD5 has known weaknesses, so using it isn't 100% secure.
     * To compensate, prefix the input buffer with its own MD5, and hash again. */
    memcpy(workbuf+1, workbuf, sizeof(workbuf)-1);
    workbuf[0] = res[0];
    MD5(workbuf, sizeof(workbuf), res);

    /* Copy the result to the output buffer */
    memcpy(output, res, 16);
}

/* Some operating systems don't have memcpy(), so include a simple implementation */
void *
memcpy(void *_dest, const void *_src, size_t n)
{
    const unsigned char *src = _src;
    unsigned char *dest = _dest;
    while (n--) *dest++ = *src++;
    return _dest;
}

这是MD5的缺陷吗?
Ali1S232'4

@Gajet,不,我使用了Linux的MD5,完全可以(AFAIK)。
ugoren

他们为什么不产生更多可能的输出?
Ali1S232'4

1
@Gajet:请考虑一下该bcopy步骤中发生的事情……这有点误导,因为实际的BSD bcopy功能在这里可以正常工作。
2012年

@han,实际上,我现在看到我bcopy的越野车。我将其更改为memcpy,然后相同的实现将变为有效。
ugoren

13

C

这可能不是最花哨的竞赛项目,但是我认为以下是任何对自己都不太聪明的编码人员可能创建的哈希函数,但对哈希函数中的操作类型却含糊其辞:

#include <stdio.h>
#include <string.h>
#include <stdint.h>

void hash(const char* s, uint8_t* r, size_t n)
{
     uint32_t h = 123456789UL;
     for (size_t i = 0; i < n; i++) {
          for (const char* p = s; *p; p++) {
               h = h * 33 + *p;
          }
          *r++ = (h >> 3) & 0xff;
          h = h ^ 987654321UL;
     }
}

int main()
{
     size_t n = 1024;
     char s[n];
     size_t m = 16;
     uint8_t b[m];
     while (fgets(s, n, stdin)) {
          hash(s, b, m);
          for (size_t i = 0; i < m; ++i)
               printf("%02x", b[i]);
          printf("\n");
     }
}

实际上,哈希函数最多可以返回L * 2048个不同的结果,其中L是可以出现的不同输入字符串长度的数量。在实践中,我在笔记本电脑上的手册页和html文档中的185万条唯一输入行上测试了该代码,仅获得了85428种不同的唯一哈希。


0

Scala:

// smaller values for more easy tests:
val len = 16
// make a 16 bytes fingerprint
def to16Bytes (l: BigInt, pos: Int=len) : List[Byte] = 
  if (pos == 1) List (l.toByte) else (l % 256L).toByte :: to16Bytes (l / 256L, pos-1)
/** if number isn't prime, take next */
def nextProbPrime (l: BigInt) : BigInt = 
  if (l.isProbablePrime (9)) l else nextProbPrime (l + 1)
/** Take every input, shift and add, but take primes */
def codify (s: String): BigInt = 
  (BigInt (17) /: s) ((a, b) => nextProbPrime (a * BigInt (257) + b))
/** very, very short Strings - less than 14 bytes - have to be filled, to obscure them a bit: */
def f (s: String) : Array [Byte] = {
  val filled = (if (s.size < 14) s + "secret" + s else s)
  to16Bytes (codify (filled + filled.reverse)).toArray.map (l => nextProbPrime (l).toByte) 
}

测试,对于相似的输入,结果看起来是否不相似:

val samples = List ("a", "aa", "b", "", "longer example", "This is a foolish, fishy test") 

samples.map (f) 

 List[Array[Byte]] = List(
Array (-41, -113, -79, 127, 29, 127, 31, 67, -19, 83, -73, -31, -101, -113, 97, -113), 
Array (-19, 7, -43, 89, -97, -113, 47, -53, -113, -127, -31, -113, -67, -23, 127, 127), 
Array (-41, -113, -79, 127, 29, 127, 31, 67, -19, 83, -73, -31, -101, -113, 97, -113), 
Array (37, -19, -7, 67, -83, 89, 59, -11, -23, -47, 97, 83, 19, 2, 2, 2), 
Array (79, 101, -47, -103, 47, -13, 29, -37, -83, -3, -37, 59, 127, 97, -43, -43), 
Array (37, 53, -43, -73, -67, 5, 11, -89, -37, -103, 107, 97, 37, -71, 59, 67))

错误仅使用质数进行编码。代替

scala> math.pow (256, 16)
res5: Double = 3.4028236692093846E38

价值观,我们以

scala> math.pow (54, 16)
res6: Double = 5.227573613485917E27

因为256以下有54个素数。


2
5.22e27 >> 2^16。没有办法暴力破解这么多可能性。
基思·兰德尔

您忘记了语言的名称
ajax333221'4

@ ajax333221:Scala。我将其添加到顶部。
用户未知

@KeithRandall:我可以“偶然地”只使用正字节,这会减少使用math.pow(27,16)的可能性,但是仍然是8e22左右。
用户未知
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.