ถอดรหัสโดยการวิเคราะห์รูปแบบ


11

คุณได้รับสตริงเข้ารหัสเข้ารหัสโดยใช้รหัสแทนง่ายมาก

ปัญหา

คุณไม่ทราบว่าตัวเลขคืออะไร แต่คุณรู้ว่า ciphertext เป็นภาษาอังกฤษและตัวอักษรที่พบบ่อยที่สุดในภาษาอังกฤษคือetaoinshrdlucmfwypvbgkqjxzตามลำดับ อักขระที่อนุญาตเท่านั้นคือตัวอักษรตัวใหญ่และช่องว่าง คุณสามารถทำการวิเคราะห์ขั้นพื้นฐาน - เริ่มจากตัวอักษรเดี่ยว แต่คุณสามารถย้ายไปยังการวิเคราะห์หลายตัวอักษรที่ซับซ้อนมากขึ้นตัวอย่างเช่น U มักตามหลังคำถาม Q เสมอและมีเพียงบางตัวอักษรเท่านั้นที่สามารถมาได้สองครั้งติดต่อกัน

ตัวอย่าง

clear : SUBMARINE TO ATTACK THE DOVER WAREHOUSE AND PORT ON TUESDAY SUNRISE
cipher: ZOQ DUPAEYSRYDSSDXVYSHEYNRBEUYLDUEHROZEYDANYKRUSYRAYSOEZNDMYZOAUPZE

clear : THE QUICK BROWN FOX BEING QUITE FAST JUMPED OVER THE LAZY DOG QUITE NICELY
cipher: TNAEPDHIGEMZQJLEVQBEMAHL EPDHTAEVXWTEODYUASEQKAZETNAERXFCESQ EPDHTAELHIARC

clear : BUFFALO BUFFALO BUFFALO BUFFALO BUFFALO BUFFALO BUFFALO
cipher: HV  WRPDHV  WRPDHV  WRPDHV  WRPDHV  WRPDHV  WRPDHV  WRP

ความท้าทาย

ดูว่าคุณสามารถถอดรหัสข้อความในแต่ละ ciphers เหล่านี้:

  • SVNXIFCXYCFSXKVVZXIHXHERDXEIYRAKXZCOFSWHCZXHERDXBNRHCXZR RONQHXORWECFHCUH
  • SOFPTGFIFBOKJPHLBFPKHZUGLSOJPLIPKBPKHZUGLSOJPMOLEOPWFSFGJLBFIPMOLEOPXULBSIPLBP KBPBPWLIJFBILUBKHPGKISFG
  • TMBWFYAQFAZYCUOYJOBOHATMCYNIAOQW Q JAXOYCOCYCHAACOCYCAHGOVYLAOEGOTMBWFYAOBFF ACOBHOKBZYKOYCHAUWBHAXOQW XITHJOV WOXWYLYCU
  • FTRMKRGVRFMHSZVRWHRSFMFLMBNGKMGTHGBRSMKROKLSHSZMHKMMMMMRVVLVMPRKKOZRMFVDSGOFRW

ฉันมีเมทริกซ์การทดแทนและ cleartext สำหรับแต่ละคน แต่ฉันจะเปิดเผยพวกเขาหากมันยากเกินไปหรือบางคนไม่เข้าใจ

โซลูชันที่สามารถถอดรหัสข้อความส่วนใหญ่ได้สำเร็จเป็นผู้ชนะ หากการแก้ปัญหาสองวิธีนั้นดีพอ ๆ กันพวกเขาจะถูกตัดสินโดยการนับคะแนน


3
นิยาม»อะไรที่สวยงามที่สุด« ฉันคิดว่านั่นเป็นสิ่งเดียวกันกับที่คริสคัดค้านในขวด 99 ขวดแล้ว มันเป็นเกณฑ์ส่วนตัวที่ค่อนข้างยากที่จะตัดสิน
Joey

@ โจอี้โหวตมากที่สุด? ให้ชุมชนตัดสินใจ
โทมัสโอ

2
Re "upvotes ที่สุด": ฉันไม่พอใจที่จะเห็นว่านี่เป็นโพสต์การประกวดยอดนิยมไม่ใช่อย่างน้อยเพราะโพสต์นั้นยอดเยี่ยมมาก ดูmeta.codegolf.stackexchange.com/questions/110/…สำหรับความคิดของฉันในเรื่องทั้งหมด
Chris Jester-Young

2
"สง่างาม" หมายถึงอะไรที่นี่ ประสิทธิภาพของ Big-O ที่ดีที่สุด?
gnibbler

1
@ Bass5098 ไม่ มันเป็นเพียงไซเฟอร์เท็กซ์ที่มีการปนเปื้อนเพื่อให้การวิเคราะห์ความถี่มีความยืดหยุ่นมากขึ้น
โทมัสโอ

คำตอบ:


9

หลาม

ฉันพบวลีลับทั้งหมดแล้ว แต่ฉันจะไม่โพสต์ที่นี่ เรียกใช้รหัสถ้าคุณสนใจ

รหัสทำงานโดยเลือกอักขระเว้นวรรคระบุการทดแทนที่เป็นไปได้ทั้งหมดสำหรับแต่ละคำจากนั้นค้นหาการแทนที่ที่เข้ากันได้ นอกจากนี้ยังช่วยให้คำศัพท์ที่ไม่อยู่ในพจนานุกรมจัดการกับคำที่สะกดผิดใน cleartext :)

ผมใช้ศัพท์ที่มีขนาดใหญ่ (~ 500K คำ) จากhttp://wordlist.sourceforge.net/

import sys,re

# get input
message = sys.argv[1]

# read in lexicon of words
# download scowl version 7.1
# mk-list english 95 > wordlist
lexicon = set()
roman_only = re.compile('^[A-Z]*$')
for word in open('wordlist').read().upper().split():
  word=word.replace("'",'')
  if roman_only.match(word): lexicon.add(word)

histogram={}
for c in message: histogram[c]=0
for c in message: histogram[c]+=1
frequency_order = map(lambda x:x[1], sorted([(f,c) for c,f in histogram.items()])[::-1])

# returns true if the two maps are compatible.
# they are compatible if the mappings agree wherever they are defined,
# and no two different args map to the same value.
def mergeable_maps(map1, map2):
  agreements = 0
  for c in map1:
    if c in map2:
      if map1[c] != map2[c]: return False
      agreements += 1
  return len(set(map1.values() + map2.values())) == len(map1) + len(map2) - agreements

def merge_maps(map1, map2):
  m = {}
  for (c,d) in map1.items(): m[c]=d
  for (c,d) in map2.items(): m[c]=d
  return m

def search(map, word_maps, outside_lexicon_allowance, words_outside_lexicon):
  cleartext = ''.join(map[x] if x in map else '?' for x in message)
  #print 'trying', cleartext

  # pick a word to try next
  best_word = None
  best_score = 1e9
  for (word,subs) in word_maps.items():
    if word in words_outside_lexicon: continue
    compatible_subs=0
    for sub in subs:
      if mergeable_maps(map, sub): compatible_subs += 1
    unassigned_chars = 0
    for c in word:
      if c not in map: unassigned_chars += 1  #TODO: duplicates?
    if compatible_subs == 0: score = 0
    elif unassigned_chars == 0: score = 1e9
    else: score = 1.0 * compatible_subs / unassigned_chars   # TODO: tweak?
    if score < best_score:
      best_score = score
      best_word = word
  if not best_word:  # no words with unset characters, except possibly the outside lexicon ones
    print cleartext,[''.join(map[x] if x in map else '?' for x in word) for word in words_outside_lexicon]
    return True

  # use all compatible maps for the chosen word
  r = False
  for sub in word_maps[best_word]:
    if not mergeable_maps(map, sub): continue
    r |= search(merge_maps(map, sub), word_maps, outside_lexicon_allowance, words_outside_lexicon)

  # maybe this word is outside our lexicon
  if outside_lexicon_allowance > 0:
    r |= search(map, word_maps, outside_lexicon_allowance - 1, words_outside_lexicon + [best_word])
  return r

for outside_lexicon_allowance in xrange(3):
  # assign the space character first
  for space in frequency_order:
    words = [w for w in message.split(space) if w != '']
    if reduce(lambda x,y:x|y, [len(w)>20 for w in words]): continue  # obviously bad spaces

    # find all valid substitution maps for each word
    word_maps={}
    for word in words:
      n = len(word)
      maps = []
      for c in lexicon:
        if len(c) != n: continue
        m = {}
        ok = 1
        for i in xrange(n):
          if word[i] in m:                      # repeat letter
            if m[word[i]] != c[i]: ok=0; break  # repeat letters map to same thing
          elif c[i] in m.values(): ok=0; break  # different letters map to different things
          else: m[word[i]]=c[i]
        if ok: maps.append(m);
      word_maps[word]=maps

    # look for a solution
    if search({space:' '}, word_maps, outside_lexicon_allowance, []): sys.exit(0)

print 'I give up.'

1

PHP (ไม่สมบูรณ์)

นี่เป็นโซลูชัน PHP ที่ไม่สมบูรณ์ซึ่งทำงานโดยใช้ข้อมูลความถี่จดหมายในคำถามรวมถึงพจนานุกรมคำที่จับคู่กับนิพจน์ทั่วไปตามตัวอักษรที่น่าเชื่อถือที่สุดในคำที่กำหนด

ปัจจุบันพจนานุกรมมีขนาดค่อนข้างเล็ก แต่ด้วยการขยายตัวที่เหมาะสมฉันคาดว่าผลลัพธ์จะดีขึ้น ฉันได้พิจารณาความเป็นไปได้ของการจับคู่บางส่วนแล้ว แต่ด้วยพจนานุกรมปัจจุบันผลลัพธ์นี้จะลดลงแทนที่จะปรับปรุงผลลัพธ์

แม้จะเป็นพจนานุกรมเล็ก ๆ ในปัจจุบันฉันคิดว่าฉันสามารถพูดได้อย่างปลอดภัยว่าข้อความที่สี่เข้ารหัสอะไร

#!/usr/bin/php
<?php

    if($argv[1]) {

        $cipher = $argv[1];

        // Dictionary
        $words = explode("/", "the/to/on/and/in/is/secret/message");
        $guess = explode("/", "..e/t./o./a../i./.s/.e..et/.ess..e");

        $az = str_split("_etaoinshrdlucmfwypvbgkqjxz");

        // Build table
        for($i=0; $i<strlen($cipher); $i++) {
            $table[$cipher{$i}]++;
        }
        arsort($table);

        // Do default guesses
        $result = str_replace("_", " ", str_replace(array_keys($table), $az, $cipher));

        // Apply dictionary
        $cw = count($words);
        for($i=0; $i<$cw*2; $i++) {
            $tokens = explode(" ", $result);
            foreach($tokens as $t) {
                if(preg_match("/^" . $guess[$i%$cw] . "$/", $t)) {
                    $result = deenc($words[$i%$cw], $t, $result);
                    echo $t . ' -> ' . $words[$i%$cw] . "\n";
                    break;
                }
            }
        }

        // Show best guess
        echo $result . "\n";

    } else {

        echo "Usage: " . $argv[0] . " [cipher text]\n";

    }

    // Quick (non-destructive) replace tool
    function deenc($word, $enc, $string) {
        $string = str_replace(str_split($enc), str_split(strtoupper($word)), $string);
        $string = str_replace(str_split($word), str_split($enc), $string);
        return strtolower($string);
    }

?>

ลองใช้ / usr / share / dict / words หากคุณใช้ระบบที่มีอยู่
Keith Randall
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.