Hashing Kata Sandi Terselubung [ditutup]

33

Dalam semangat Kontes C yang curang , saya memulai Kontes Code yang curang. Tujuan dari kontes ini adalah untuk langsung menerapkan beberapa kode, sambil menyembunyikan bug jahat di dalamnya.

Kontes

Anda adalah tikus Rusia rahasia yang bekerja di departemen TI agen mata-mata Amerika. Bos Amerika Anda baru saja meminta Anda untuk menerapkan algoritma hashing kata sandi yang akan digunakan untuk mengenkripsi pesan rahasia.

Bos Anda ingin Anda menerapkan fungsi berikut:

f: String -> byte[16]

yang mengubah kata sandi menjadi kuantitas 16-byte yang cocok untuk digunakan sebagai kunci AES. Bos Anda menginginkan fungsi yang aman, yang dalam konteks ini berarti bahwa rangkaian kata sandi yang berbeda harus menghasilkan hasil yang berbeda dengan probabilitas yang luar biasa. Misalnya, mengembalikan hash md5 dari input akan menjadi implementasi sederhana f.

Tentu saja, bos Anda yang sebenarnya di agen mata-mata Rusia ingin Anda menumbangkan proses ini. Tugas Anda adalah mengimplementasikan fsedemikian rupa sehingga Rusia dapat mendekripsi semua pesan rahasia yang dienkripsi menggunakan kunci yang dikembalikan oleh f.

Untuk melakukannya, Anda harus menerapkan fsehingga hanya mengembalikan sebagian kecil dari 2 ^ 128 output yang mungkin. Khususnya, Anda fharus mengembalikan paling banyak 2 ^ 16 hasil yang berbeda sehingga Rusia dapat melakukan pencarian dengan mudah untuk mencari kunci yang benar untuk setiap pesan terenkripsi yang ingin mereka dekripsi.

Namun perlu diingat bahwa spionase membawa hukuman mati. Agar tidak ketahuan, fungsi Anda fharus menghasilkan setidaknya 2 ^ 8 hasil yang berbeda, sehingga inspeksi sepintas dari beberapa output ftidak mungkin untuk mengungkapkan duplikat. Dan yang paling penting, kode yang Anda perkenalkan untuk membatasi rentang fharus terlihat tidak disengaja, tidak disengaja. Jika Anda pernah dibawa ke ruang sidang, pasti ada keraguan yang masuk akal apakah Anda memperkenalkan bug secara sengaja atau tidak sengaja.

Menilai

Saya dan dua orang lain yang saya rekrut akan menilai entri (email saya jika Anda ingin menilai). Saya menawarkan hadiah 200 reputasi untuk entri yang menang. Kiriman harus diunggah sebelum 1 Mei.

Menilai akan mempertimbangkan kriteria berikut:

  • Apakah fmematuhi spesifikasi, yaitu apakah itu menghasilkan antara 2 ^ 8 dan 2 ^ 16 output yang mungkin. Jangan merasa ini adalah batas yang sulit, tetapi kami akan mengurangi poin jika Anda terlalu jauh dari jangkauan.
  • Apakah bug itu masuk akal akibat kesalahan yang tidak disengaja?
  • Apakah output fterlihat acak?
  • Semakin pendek implementasi Anda f, semakin baik.
  • Semakin jelas implementasi Anda f, semakin baik.

Catatan

Anda dapat menggunakan bahasa apa pun untuk menerapkan kode Anda. Anda mencoba menyembunyikan bug di depan mata, jadi kode yang dikaburkan tidak disarankan.

Anda mungkin ingin melihat beberapa pemenang kontes C sebelumnya yang curang untuk merasakan apa yang membuat pengajuan yang baik.

String input akan dapat dicetak ascii (32 hingga 126, inklusif). Anda dapat mengasumsikan panjang maksimal yang masuk akal jika Anda mau.

Keith Randall
sumber
1
apakah ada batasan pada string input? seperti itu hanya terdiri dari alfabet?
Ali1S232
@Gajet: Anda harus menangani semua karakter ascii yang dapat dicetak (32 hingga 126, inklusif).
Keith Randall
Apakah rentang output semua string 16-byte, atau hanya yang dapat dicetak?
stan
@ boothby: semua kemungkinan nilai 16-byte (2 ^ 128 kemungkinan)
Keith Randall
1
Saya memberikan suara untuk menutup pertanyaan ini sebagai di luar topik karena tantangan curang tidak lagi pada topik di situs ini. meta.codegolf.stackexchange.com/a/8326/20469
cat

Jawaban:

15

C

2 ^ 16 kemungkinan keluaran (atau 2 ^ 8 kali jumlah karakter yang digunakan).
Menggunakan implementasi MD5 Linux, yaitu AFAIK, baik. Tapi ini memberikan hash yang sama, misalnya, untuk "40" dan "42".
EDIT: Berganti nama bcopymenjadi memcpy(parameter bertukar tentu saja).
EDIT: Dikonversi dari program ke fungsi, untuk memenuhi persyaratan yang lebih baik.

#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;
}
ugoren
sumber
apakah ini cacat dengan MD5?
Ali1S232
@ Gajet, Tidak, saya menggunakan MD5 Linux, yang benar-benar OK (AFAIK).
ugoren
mereka mengapa tidak menghasilkan lebih banyak output yang mungkin?
Ali1S232
1
@Gajet: Pertimbangkan apa yang terjadi pada bcopylangkah ... ini adalah sedikit penyesatan, karena bcopyfungsi BSD yang sebenarnya akan berfungsi dengan baik di sini.
han
@han, Sebenarnya, sekarang saya melihat bahwa saya bcopybuggy. Saya akan mengubahnya memcpy, dan implementasi yang sama akan menjadi valid.
ugoren
13

C

Ini mungkin bukan entri kontes yang paling mencolok, tapi saya pikir yang berikut ini adalah jenis fungsi hash yang bisa dibuat oleh pembuat kode yang terlalu pintar untuk kebaikan mereka sendiri, dengan gagasan samar-samar tentang jenis operasi yang Anda lihat dalam fungsi hash:

#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");
     }
}

Bahkan fungsi hash dapat mengembalikan tidak lebih dari L * 2048 hasil yang berbeda, di mana L adalah jumlah panjang string input yang berbeda yang dapat terjadi. Dalam prakteknya, saya menguji kode pada 1,85 juta baris input unik dari halaman manual dan dokumen html di laptop saya, dan hanya mendapatkan 85428 hash unik yang berbeda.

han
sumber
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) 
}

Tes, jika hasilnya tidak mirip dengan input yang sama:

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))

Kesalahan menggunakan bilangan prima hanya untuk pengkodean. Dari pada

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

nilai, kita akhiri dengan

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

karena ada 54 bilangan prima di bawah 256.

Pengguna tidak diketahui
sumber
2
5.22e27 >> 2^16. Tidak ada cara untuk memaksa sebanyak mungkin kemungkinan itu.
Keith Randall
Anda lupa nama bahasa
ajax333221
@ ajax333221: Scala. Saya menambahkannya ke atas.
pengguna tidak diketahui
@KeithRandall: Saya 'tidak sengaja' hanya dapat menggunakan Bytes positif, yang akan mengurangi kemungkinan untuk math.pow (27, 16), tapi itu masih sekitar 8e22.
pengguna tidak diketahui