アンダーハンドパスワードハッシュ[終了]


33

Underhanded C Contestの精神で、私はUnderhanded Code Contestを始めています。このコンテストの目的は、いくつかのコードを簡単に実装し、その中に微妙なバグを隠すことです。

コンテスト

あなたはアメリカのスパイ機関のIT部門で働いているロシアの秘密のほくろです。アメリカの上司から、秘密メッセージの暗号化に使用するパスワードハッシュアルゴリズムの実装を求められました。

上司は、次の機能を実装することを望んでいます。

f: String -> byte[16]

パスワードをAESキーとしての使用に適した16バイトの数量に変換します。上司は安全な機能を求めています。これは、このコンテキストでは、異なるパスワード文字列が圧倒的な確率で異なる結果を生成することを意味します。たとえば、入力のmd5ハッシュを返すことは、の単純な実装になりfます。

もちろん、ロシアのスパイ機関の本当のボスは、このプロセスを破壊することを望んでいるでしょう。あなたの仕事はf、ロシア人がによって返されfたキーを使用して暗号化されたすべての秘密メッセージを解読できるように実装することです。

そのためにfは、2 ^ 128の可能な出力の小さなサブセットのみを返すように実装する必要があります。特に、fロシア人が解読したい暗号化されたメッセージごとに正しいキーを簡単にブルートフォース検索できるように、最大​​2 ^ 16の異なる結果を返す必要があります。

ただし、スパイ行為には死刑が適用されることに留意してください。キャッチされないようにするには、関数fが少なくとも2 ^ 8の異なる結果を生成する必要があります。そのため、いくつかの出力の大まかな検査でf重複が明らかになることはほとんどありません。 そして最も重要なことは、範囲を制限するために導入するコードはf、意図的なものではなく、意図的なものではないことです。 法廷に連れて行かれた場合、意図的にバグを導入したのか、偶然にバグを導入したのかについて、ある程度の疑いがなければなりません。

審査

私と私が募集する他の2人が応募作品を審査します(審査を希望する場合はメールしてください)。受賞作品に対して200の評判賞金を提供しています。提出物は5月1日までにアップロードする必要があります。

審査では、次の基準が考慮されます。

  • f仕様に準拠しています。つまり、2 ^ 8〜2 ^ 16の可能な出力を生成します。これらが厳しい制限であるとは思わないでください。ただし、範囲外である場合はポイントを差し引きます。
  • バグは、意図しない間違いの結果であると思われますか?
  • 出力はfランダムに見えますか?
  • の実装が短いほどf優れています。
  • の実装が明確であればfあるほど、より良い結果が得られます。

ノート

任意の言語を使用してコードを実装できます。バグを目立たないように隠そうとしているので、難読化されたコードは推奨されません。

過去のUnderhanded Cコンテストの受賞者の一部を見て、優れた提出物の感触をつかむことができます。

入力文字列は、印刷可能なASCII(32から126まで)になります。必要に応じて、適切な最大長を想定できます。


1
入力文字列に制限はありますか?アルファベットだけで構成されているように?
Ali1S232

@Gajet:印刷可能なすべてのASCII文字(32から126まで)を処理する必要があります。
キースランドール

出力範囲はすべて16バイト文字列ですか、それとも単に印刷可能なものですか?
ブースビー

@boothby:すべての可能な16バイト値(2 ^ 128の可能性)
キースランドール

1
このサイトでは、人手不足の課題がトピックではなくなったため、この質問をトピック外として終了することに投票しています。meta.codegolf.stackexchange.com/a/8326/20469
cat

回答:


15

C

2 ^ 16の可能な出力(または使用されている文字数の2 ^ 8倍)。
LinuxのMD5実装を使用します。これは、わかりました。ただし、これにより、たとえば「40」と「42」に対して同じハッシュが得られます。
編集:名前bcopyを変更しましたmemcpy(もちろん、交換されたパラメーター)。
編集:要件を満たすために、プログラムから機能に変換されました。

#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を使用しました。
-ugoren

彼らはなぜそれがより多くの可能な出力を生成しないのですか?
Ali1S232

1
@Gajet:bcopyステップで何が起こるか考えてみてください...実際のBSD bcopy機能はここで適切に機能するので、それはちょっとした方向転換です。
ハン

@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

@ ajax333221:Scala。トップに追加しました。
ユーザー不明

@KeithRandall:「偶然」正のバイトしか使用できなかったため、math.pow(27、16)の可能性が減りましたが、それでも8e22程度です。
ユーザー不明
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.