מבוא כללי לתכנות ולמדעי המחשב 1843-0310 מרצה: אמיר רובינשטיין מתרגל: דין שמואל אוניברסיטת תל אביב סמסטר חורף 2017-8 )substitution cipher( )חלק :)II צופן החלפה שיעור 9 מבוא לדיחסת מידע 1) מבוא לקריפטוגרפיה 2) 1
מבוא לדחיסה )compression( יילמד מתוך,CS Field Guide הפרק על coding compression שאר הפרקים גם כן מומלצים בחום )חלק על נושאים שלמדנו, עשוי לשפר את ההבנה( פרוייקט דומה שתורגם לעברית, ע"י פרופ' בני שור ופרופ' שמעון שוקן(: מדעי המחשב ללא מחשב 2
מבוא לקריפטוגרפיה חלק II 3
תזכורת קריפטוגרפיה - מונחים בסיסיים Plain text Decryption פענוח Encryption הצפנה Cipher/code צופן / קוד Cipher text תכונה נדרשת: קלים Encryption+Decryption עבור הצדדים המעורבים, קשים אבל עבור יריב שמאזין לתשדורת. פתרון אפשרי: לצדדים המעורבים יהיה סוד משותף כלשהו..)code breaking( גילוי הסוד המשותף ופענוח ה- cipher text נקרא פיצוח/שבירת הצופן 4
)Caesar code( תזכורת צופן קיסר הסוד המשותף לשני הצדדים הוא מספר שלם כלשהו שנקרא היסט )offset( מסדרים את התווים בסדר כלשהו, ומחליפים כל תו בזה שנמצא מספר מקומות מימינו בהתאם לגודל ה-.offset ההחלפה היא מעגלית. למשל עבור :offset=3 Image from: http://www.maths-resources.net נניח כי הטקסט שלנו יכול להכיל תווים נוספים מלבד.A-Z מכנים את קבוצת התווים שהטקסט יכול להכיל בשם אלפבית.)alphabet( במימוש בפייתון שנראה מייד נשתמש בא"ב הכולל את כל 128 תווי ה-.ASCII 5
צופן קיסר )אי( דיון על יעילות הצופן כפי שראינו, צופן קיסר ניתן לפריצה באופן טריוויאלי* וביעילות )ולכן אינו בטוח(. למעשה אין לצופן הזה שום שימוש מעשי כיום בהצפנה )אבל כן בהוראה...(. * טריוויאלי הכוונה שאלגוריתם הפריצה אינו מתוחכם כלל, ופשוט עובר על כל האפשרויות בזו אחר זו. 6 אלגוריתם כזה נקרא "כח גס" force(,)brute או "חיפוש ממצה" search(.)exhaustive
צופן החלפה cipher( )substitution הכללה של צופן קיסר: כל תו מוחלף בתו כלשהו )לאו דווקא בהיסט קבוע(. שימו לב כי המיפוי )למה?( מהתווים המקוריים לתווים המחליפים חייב להיות פונקציה חד-חד ערכית לשם פשטות, נניח הא"ב מכיל רק 26 האותיות הקטנות באנגלית.)a-z( המחליפים תהיה גם כן.a-z נניח כי כל תו אחר יישאר ללא שינוי. קבוצת התווים 7 כמה צפני החלפה שונים אפשריים בתנאים אלו? האם שבירת הצופן בשיטת brute-force אפשרית כאן? )הניחו שבידיכם מחשב שמסוגל לבחון 10 מיליארד מיפויים בשנייה, ואם מתקבל טקסט באנגלית הוא מודיע לנו על הצלחת שבירת ההצפנה(. אין בכל זאת ניתן לשבור את הצופן? תשובות, חישובים, ודיון בכיתה.
שבירת צופן החלפה באמצעות מאפיינים של השפה שכיחות אותיות באנגלית )מויקיפדיה(: זו כמובן שכיחות ממוצעת על פני טקסטים מסוימים שנבחרו לצורך הסטטיסטיקה. בכל טקסט ספציפי סביר שיהיו הבדלים. מדוע שימוש בשכיחויות האותיות לצורך שבירת הצופן הוא בעייתי עבור טקסטים קצרים? אילו עוד מאפיינים של שפת הטקסט המוצפן ניתן לנצל? 8
מימוש צופן החלפה בפייתון נדגים באמצעות פייתון הצפנה, פענוח ושבירה של צופן החלפה. בחלק מהמשימות ניכנס לפרטי המימוש, בחלק אחר נשתמש בפונקציות שחורה. חלק יוצגו בכיתה וחלק תידרשו להבין בתרגיל הבית. כקופסה "אימון" sample( )training תכנון המשימות: יצירת מיפוי אותיות אקראי 1. הצפנה טקסט נתון 2. פענוח )עם הצופן( 3. שבירה )ללא ידיעת הצופן( 4. ספירת תדירויות של אותיות במדגם A. ניסיון פענוח טקסט אמיתי B. 9
מילונים תזכורת מתרגיל בית 3 מילון )dict( בפייתון הוא טיפוס נתונים שמכיל זוגות איברים מפתח:ערך. הם שימושיים לשמירת מיפוי מפתחות הם בין שתי קבוצות ערכים ייחודיים )מפתח לא יכול להופיע פעמיים( >>> d = {"France":"Europe", "Genrmay":"Europe", "Japan":"Asia"} >>> type(d) <class 'dict'> >>> d["japan"] 'Asia' >>> d["israel"] Traceback (most recent call last): File "<pyshell#1>", line 1, in <module> d["israel"] KeyError: 'Israel' >>> d["israel"] = "Europe" >>> d["israel"] 'Europe' >>> d["europe"] Traceback (most recent call last): File "<pyshell#1>", line 1, in <module> d["europe"] KeyError: 'Europe' 10
1. יצירת מיפוי אותיות אקראי alphabet = "abcdefghijklmnopqrstuvwxyz" import random def create_cipher(alphabet): """ create asubstitution cipher in a dictionary """ shuffled = random.sample(alphabet,26) shuffled = "".join(shuffled) # convert list to str, #NO NEED TO UNDERSTAND THIS print("original: ", alphabet) # before print("shuffled: ", shuffled) # after encrypt_dict = {} #an empty dictionary for i in range(len(original)): encrypt_dict[original[i]] = shuffled[i] _ return encrypt_dict 11
2. הצפנת טקסט נתון def encrypt(text, enc_dict): """ encrypts text using enc_dict as substitution cipher """ cipher = "" for char in text: if char not in enc_dict: cipher = cipher + char #characters not in #enc_dict are untouched else: cipher = cipher + enc_dict[char] return cipher 12
פענוח, 3. עם הצופן def reverse_dict(d1): """ reverse keys <--> values in dictionary d1 """ d2 = {} #an empty dictionary for key in d1: value = d1[key] d2[value] = key #insert value:key into d2 return d2 def decrypt(cipher, enc_dict): """ decrypts cipher that was encrypted using enc_dict """ dec_dict = create_reverse_cipher(enc_dict) return encrypt(cipher, dec_dict) 13
4. שבירה )ללא ידיעת הצופן( 14
ספירת תדירויות של אותיות במדגם "אימון" sample( )training def char_count(text): ''' counts number of appearances of characters in text. Returns a sorted dictionary. ''' cnts = {} for char in alphabet: cnts[char] = 0 for char in text: if char in alphabet: cnts[char] = cnts[char] + 1 return cnts def sort_by_count(cnts_dict): ''' sorts a given dictionary whose elements are char:count sorting is done by counts, returns a list ''' return sorted(cnts_dict.items(), key = lambda x:-x[1]) 15
ספירת תדירויות של אותיות בטקסט "אימון" sample( )training Executions: >>> d = char_count("how much wood would a woodchuck chuck") >>> d {'n': 0, 'l': 1, 'h': 3, 's': 0, 'b': 0, 'v': 0, 'j': 0, 'e': 0, 'c': 5, 'a': 1, 'x': 0, 'o': 6, 'k': 2, 'u': 4, 'g': 0, 'p': 0, 't': 0, 'm': 1, 'w': 4, 'y': 0, 'f': 0, 'd': 3, 'q': 0, 'z': 0, 'r': 0, 'i': 0} >>> d['w'] 4 >>> d = sort_by_count(d) >>> d [('o', 6), ('c', 5), ('u', 4), ('w', 4), ('h', 3), ('d', 3), ('k', 2), ('l', 1), ('a', 1), ('m', 1), ('n', 0), ('s', 0), ('b', 0), ('v', 0), ('j', 0), ('e', 0), ('x', 0), ('g', 0), ('p', 0), ('t', 0), ('y', 0), ('f', 0), ('q', 0), ('z', 0), ('r', 0), ('i', 0)] 16
17
ספירת תדירויות של אותיות בטקסט "אימון" sample( )training from urllib.request import urlopen הורדת טקסט מהרשת )אין צורך להבין, רק לדעת להשתמש בזה(: def download(url): '''url should be a string containing the full path, incl. http://''' f = urlopen(url) btext = f.read() text = btext.decode('utf-8') #read from the object, storing the page's contents in text. f.close() return text Executions: #Tom Sawyer tom = download ("http://www.gutenberg.org/cache/epub/74/pg74.txt") #Romeo and Juliet romeo = download ("http://www.gutenberg.org/cache/epub/1513/pg1513.txt") #New York Times nyt = download("http://www.nytimes.com") 18
הרצות על טקסטים אמיתיים #Tom Sawyer >>> tom = download("http://www.gutenberg.org/cache/epub/74/pg74.txt") >>> tom_count = char_count(tom) >>> tom_count = sort_by_count(tom_count) >>> len(tom) 421872 >>> tom_count [('e', 37791), ('t', 28744), ('o', 24663), ('a', 24160), ('n', 21166), ('h', 19350), ('i', 18689), ('s', 18140), ('r', 16519), ('d', 15338), ('l', 12591), ('u', 9552), ('w', 7808), ('m', 7397), ('c', 7053), ('y', 6924), ('g', 6904), ('f', 6342), ('p', 4826), ('b', 4795), ('k', 3188), ('v', 2493), ('j', 474), ('x', 336), ('q', 183), ('z', 161)] #New York Times nyt = download("http://www.nytimes.com") nyt_count = char_count(nyt) nyt_count = sort_by_count(nyt_count) >>> len(nyt) 167340 >>> nyt_count [('e', 7613), ('t', 7482), ('a', 6965), ('i', 6482), ('s', 6254), ('o', 5194), ('n', 5160), ('l', 4807), ('r', 4477), ('c', 3943), ('d', 3372), ('m', 3044), ('h', 3023), ('p', 2265), ('y', 1824), ('u', 1740), ('w', 1459), ('g', 1425), ('v', 1022), ('f', 984), ('b', 866), ('k', 511), ('x', 325), ('j', 161), ('q', 98), ('z', 91)] 19