Underhanded Password Hashing [닫힘]


33

Underhanded C Contest 의 정신으로 Underhanded Code Contest를 시작하고 있습니다. 이 콘테스트의 목적은 일부 코드를 간단하게 구현하는 동시에 사악한 버그를 미묘하게 숨기는 것입니다.

콘테스트

당신은 미국 스파이 기관의 IT 부서에서 일하는 비밀 러시아 사마귀입니다. 미국 상사가 방금 비밀 메시지 암호화에 사용할 암호 해싱 알고리즘을 구현하도록 요청했습니다.

상사는 다음 기능을 구현하기를 원합니다.

f: String -> byte[16]

암호를 AES 키로 사용하기에 적합한 16 바이트 수량으로 변환합니다. 상사는 안전한 기능을 원합니다.이 맥락에서 다른 암호 문자열은 압도적 인 확률로 다른 결과를 생성해야 함을 의미합니다. 예를 들어 입력 의 md5 해시를 반환하면 간단한 구현이 f됩니다.

물론 러시아 스파이 기관 의 실제 상사는이 과정을 파괴하기를 원할 것입니다. 귀하의 임무는 f러시아인이에서 반환 한 키를 사용하여 암호화 된 모든 비밀 메시지를 해독 할 수 있도록 구현 하는 것 f입니다.

그렇게하려면 f2 ^ 128 개의 가능한 출력 중 작은 서브 세트 만 리턴하도록 구현해야합니다 . 특히, f러시아인이 해독하려는 각 암호화 된 메시지에 대해 올바른 키를 쉽게 무차별 검색 할 수 있도록 최대 2 ^ 16 개의 서로 다른 결과를 반환해야합니다.

그러나 간첩 행위에는 사형이 따른다는 점을 명심하십시오. 잡히지 않으려면 함수 f가 적어도 2 ^ 8 개의 서로 다른 결과를 생성해야하므로 몇 개의 출력에 대한 커서 검사가 f중복을 나타내지 않을 수 있습니다. 그리고 가장 중요한 것은 범위를 제한하기 위해 도입 한 코드 f는 의도적이지 않고 의도적으로 보이지 않아야합니다. 법정에 들어간 적이 있다면 의도적으로 또는 실수로 버그를 도입했는지에 대한 합리적인 의심이 있어야합니다.

심사

저와 제가 채용 한 다른 두 사람이 출품작을 심사 할 것입니다 (심판을 원하시면 저에게 이메일을 보내십시오). 우승작에 대해 200 개의 평판 현상금을 제공하고 있습니다. 제출물은 5 월 1 일까지 업로드해야합니다.

심사는 다음 기준을 고려합니다.

  • 않는 f사양을 준수 즉은 16 ^ 2 ^ 8 내지 2 가능한 출력을 생성한다. 이러한 제한이 있다고 생각하지 않지만 범위를 벗어난 경우 포인트를 차감합니다.
  • 의도하지 않은 실수로 인한 버그일까요?
  • 출력이 f무작위로 보입니까?
  • 구현 시간이 짧을수록 f좋습니다.
  • 구현이 명확 f할수록 좋습니다.

노트

코드를 구현하기 위해 모든 언어를 사용할 수 있습니다. 눈에 잘 띄는 버그를 숨기려고하므로 난독 화 된 코드는 권장되지 않습니다.

이전 Underhanded C 컨테스트 수상자 중 일부를 살펴보고 좋은 제출 방법에 대해 알아보십시오.

입력 문자열은 ASCII로 인쇄 할 수 있습니다 (32 ~ 126 포함). 원하는 경우 합리적인 최대 길이를 가정 할 수 있습니다.


1
입력 문자열에 제한이 있습니까? 알파벳만으로 구성되어 있습니까?
Ali1S232

@Gajet : 인쇄 가능한 모든 ASCII 문자 (32 ~ 126 포함)를 처리해야합니다.
Keith Randall

출력 범위가 모두 16 바이트 문자열입니까 아니면 인쇄 가능한 문자열입니까?
부스비

@boothby : 가능한 모든 16 바이트 값 (2 ^ 128 가능성)
Keith Randall

1
나는이 문제를 주제로 다루지 않기로 결심했다. 왜냐하면이 사이트에서 미숙 한 도전은 더 이상 주제가 아니기 때문이다. meta.codegolf.stackexchange.com/a/8326/20469
cat

답변:


15

기음

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

@Gajet, 아니요, Linux의 MD5를 사용했는데 완벽합니다 (AFAIK).
ugoren

더 많은 출력을 생성하지 않는 이유는 무엇입니까?
Ali1S232

1
@Gajet :이 bcopy단계 에서 어떤 일이 발생하는지 고려해보십시오 . 실제 BSD bcopy함수가 여기서 제대로 작동하기 때문에 약간 잘못된 방향 입니다.
han

@han, 사실, 이제는 내 bcopy버그가 있음을 알 수 있습니다. 로 변경 memcpy하면 동일한 구현이 유효 해집니다.
ugoren

13

기음

이것은 가장 화려한 콘테스트 출품작이 아닐 수도 있지만 다음은 해시 함수에서 볼 수있는 작업 종류에 대한 모호한 아이디어로 코더가 자신의 이익을 위해 너무 영리하게 만들 수있는 해시 함수의 종류입니다.

#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

스칼라 :

// 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. 그렇게 많은 가능성을 무너 뜨릴 방법이 없습니다.
Keith Randall

당신은 언어의 이름을 잊었다
ajax333221

@ ajax333221 : 스칼라. 나는 그것을 상단에 추가했습니다.
사용자가 알 수 없음

@KeithRandall : 나는 실수로 양의 바이트 만 사용할 수 있습니다. 이는 math.pow (27, 16)의 가능성을 줄이지 만 여전히 약 8e22입니다.
사용자가 알 수 없음
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.