Lekcja 7 - szyfrowanie i hashe (notatki)

Lekcja 7 - szyfrowanie i hashe (notatki)

Zadanie - kwestionariusz

1) Dane uczestników przechowuj w słowniku skonstruowanym wg schematu imię : wynik np. wyniki = {"Janek" : 24}.

wyniki = {}
wyniki[gracz] = punkty
punkty = 0

Poproś użytkownika o wprowadzenie imienia:

gracz = input("Podaj swoje imię: ")
print(f"Witaj {gracz}!")

Przeanalizuj poniższą strukturę danych. Dopisz kolejne pytania z odpowiedziami i punktami - fajnie jak by było min 5 pytań.

#przykład
ankieta = [
    (
		"Który z poniższych języków programowania uznany został za najpopularniejszych wg . rankingu Tiobe 2023?",
        [
            ("Cobol", 0),
            ("Pascal", 0),
            ("Python", 1),
        ],
    ),

    (
        "Która z poniższych metod zabezpieczenia 2FA uznawana jest za najbezpieczniejszą?",
        [
            ("SMS", 0),
            ("kod z aplikacji (TOTP)", 0),
            ("klucz sprzętowy", 1),
        ],
    ),
        (
        "Skrót Iot oznacza",
        [
            ("Instrument of Technology", 0),
            ("Informacje o Technologii", 0),
            ("Internet of Things (Internet Rzeczy)", 1),
        ],
        ]

A zatem mam zmienną "ankieta" do której przypisano listę pytań i odpowiedzi. Każde zestaw tj. pytanie i odpowiedzi zamknięte są w krotce. Każde pytanie jest pierwszym elementem tej krotki (zatem stoi na pozycji 0), po nim następuje lista z odpowiedziami (pozycja 1), wewnątrz której są zestawy trzech odpowiedzi wraz z ich punktacją zamknięte w krotkach. Zgodnie ze wzorem:

zmienna = [(pytanie [(odp1, 0), (odp2, 0), (odp3, 1)])]

Dopisz kod, który będzie wyświetlał pytania:

for zestaw in ankieta:
    print(zestaw[0]) 

Wprowadzamy zmienną "zestaw" w pętli for (nie while ponieważ liczba pytań będzie znana). Można powiedzieć, że odtąd będą w nim przechowywane dwuelementowe zestawy, zgodnie ze wzorem: (pytanie [(odp1, 0), (odp2, 0), (odp3, 1)]). Po czym drukujemy pierwszy element (pozycja 0) na liście czyli pytanie.

Następnie musimy wyświetlić odpowiedzi, które będą numerowane od 0-3. Stąd konieczność wprowadzenia nowej zmiennej "numer_odp". Po czym drukujemy odpowiedzi. By to zrobić trzeba wprowadzić nową pętlę - jako, że trzeba wydrukować tyle razy ile jest odpowiedzi - i zmienną "odpowiedzi" pod którą będą przechowywane kolejne krotki: (odp1, 0), (odp2, 0), (odp3, 1).

Stąd odwołanie do zestawu, w którym odpowiedzi znajdują się na drugim miejscu w krotce po pytaniu (pozycja 1): [(odp1, 0), (odp2, 0), (odp3, 1)] A następnie nakaz wydrukowania zmiennej numer_odp (zaczynającej się od zera) wraz ze zmienną "odpowiedzi" czyli (odp1, 0). Ponieważ nie chcemy by wyświetlało odpowiedź i punktację naraz wskazujemy, że chodzi nam tylko o pierwszy element krotki (pozycja 0) czyli odp1. Przy pierwszym przejściu pętli wydrukuje: 0 odp1, potem numer_odp wzrośnie o 1 i przeskoczymy na kolejną krotkę zatem wydrukuje: 1 odp2 itd.

    numer_odp = 0
    for odpowiedzi in zestaw[1]:
      print(f"{numer_odp} {odpowiedzi[0]}")
      numer_odp += 1

Kolejnym krokiem jest zażądanie odpowiedzi od gracza, którą powinien wprowadzić jako numer:

    odpowiedz_gracza = int(input("Twoja odpowiedź to: "))
    while odpowiedz_gracza not in (0,1,2,3):
      print("Zły wybór, musisz podać numer odpowiedzi.")
      odpowiedz = input("Twoja odpowiedź to: ")

Pętla while będzie się powtarzać tak długo, aż gracz nie wpisze numeru odpowiedzi.

Oraz zliczyć punkty. aby sięgnąć po punkty musimy odwołać się do ankiety do zestawu[1] czyli odpowiedzi np. [(odp1, 0), (odp2, 0), (odp3, 1)] następnie sprawdzamy krotkę, którą wybrał gracz stąd odwołanie do zmiennej "odpowiedz_gracza" (patrz wiersze wyżej). Pod tą zmienną kryje się liczba odwołująca nas do jednej z trzech krotek np. gdy gracz wybierze "0" uzyskamy: (odp1, 0) na końcu dodajemy [1], ponieważ pobiera drugi element krotki z odpowiedzią czyli punkty przyznawane za daną odpowiedź. Potem dodajemy punktację pytania do ogólnego wyniku gracza (czyli zmiennej "punkty" z początku kodu):

    punkty += zestaw[1][odpowiedz_gracza][1]

Gdzie odpowiedz_gracza jest liczbą wskazującą, do której z kolei krotki odnosi się polecenie.

Wszystko to dzieje się w oparciu o schemat:

W cudzysłowach zmienne zastosowane w kwestionariuszu.

Na koniec wyświetlamy uzyskany wynik:

print(f"Twój wynik {gracz} to {punkty}")
print("\n---Wyniki---")
for gracz, punkty in ranking.items():
    print(f"{gracz} zdobył {punkty}")

Zakończony kwestionariusz wygląda tak:

wyniki = {}
wyniki[gracz] = punkty
punkty = 0

gracz = input("Podaj swoje imię: ")
print(f"Witaj {gracz}!")

ankieta = [
    (    "Kiedy odbyła się bitwa pod Grunwaldem?",
        [
            ("1410", 1),
            ("1310", 0),
            ("1352", 0),
        ],
    ),
    (    "Kiedy wybuchła druga wojna światowa?",
        [
            ("1945", 0),
            ("1914", 0),
            ("1939", 1),
        ],
    ),
    (    "Kiedy wybrano Polaka na papieża?",
        [
            ("1987", 0),
            ("1992", 0),
            ("1978", 1),
        ],
    ),
    (    "Kiedy wybuchła rewolucja francuska?",
        [
            ("1889", 0),
            ("1799", 0),
            ("1789", 1),
        ],
    ),
    (    "Kiedy zakończyła sie pierwsza wojna światowa?",
        [
            ("1945", 0),
            ("1989", 0),
            ("1918", 1),
        ],
    ),    
]

for zestaw in ankieta:
    print(zestaw[0]) 
    numer_odp = 0
    for odpowiedzi in zestaw[1]:
      print(f"{numer_odp} {odpowiedzi[0]}")
      numer_odp += 1
    odpowiedz_gracza = int(input("Twoja odpowiedź to: "))
    while odpowiedz_gracza not in (0,1,2,3):
      print("Zły wybór, musisz podać liczbę.")
      odpowiedz = input("Twoja odpowiedź to: ")
    punkty += zestaw[1][odpowiedz_gracza][1]

print(f"Twój wynik {gracz} to {punkty}")
print("\n---Wyniki---")
for gracz, punkty in ranking.items():
    print(f"{gracz} zdobył {punkty}")

Szyfrowanie

Przekazywania wiadomości w sposób poufny, czyli wprowadzenie danych wyjściowych, zmiana za pomocą różnego rodzaju klucza oraz odcyfrowanie.

Funkcja skrótu (hashe)

Też zamienia dane źródłowe w coś czego nie da się odczytać, ale w przeciwieństwie do szyfrowania jest to proces nieodwracalny. Czemu więc służy? Tak długo jak funkcja podaje identyczną wartość tak długo mamy pewność, że w pliku źródłowym nic nie uległo zmianie. W ten sposób weryfikuje się m.in. maile lub programy z internetu. W wypadku tych ostatnich na stronie www umożliwiającej pobranie programu z Internetu znajduje się CHECKSUM, podający hashe danego programu. Jeśli zweryfikujemy ten numer z tym, który otrzymamy po zastosowaniu funkcji hashe na pobranych danych i będzie odmienny możemy założyć, że albo ktoś zhakował plik na serwerze i nie wolno go uruchamiać, lub źle się ściągnęło i może nie działać.

Zadanie domowe

Stwórz szyfr cezara przy użyciu pętli for.

ZNAKI = [
    "a",
    "b",
    "c",
    "d",
    "e",
    "f",
    "g",
    "h",
    "i",
    "j",
    "k",
    "l",
    "m",
    "n",
    "o",
    "p",
    "q",
    "r",
    "s",
    "t",
    "u",
    "v",
    "w",
    "x",
    "y",
    "z",
    "A",
    "B",
    "C",
    "D",
    "E",
    "F",
    "G",
    "H",
    "I",
    "J",
    "K",
    "L",
    "M",
    "N",
    "O",
    "P",
    "Q",
    "R",
    "S",
    "T",
    "U",
    "V",
    "W",
    "X",
    "Y",
    "Z",
    "Ą",
    "ą",
    "Ć",
    "ć",
    "Ę",
    "ę",
    "Ł",
    "ł",
    "Ń",
    "ń",
    "Ó",
    "ó",
    "Ś",
    "ś",
    "Ź",
    "ź",
    "Ż",
    "ż",
    " ",
    ".",
    "?",
    "!",
]

print("""Witaj w programie do szyfrowania!
      Chcesz:
      a. zaszyfrować wiadomość 
      b. odszyfrować wiadomość?""")
      
wybor = input("Wybierz a lub b: ")
while wybor.lower() not in ("a", "b"):
      print("Musisz podać a lub b")
      wybor = input("Co chcesz zrobić?: ")

if wybor.lower() == "a": #szyfrowanie
    print("Podaj tekst, który chcesz zaszyfrować.")
    tekst = input("Twój tekst: ")
    print("Teraz wybierz swój klucz, czyli o ile znaków chcesz przeskoczyć.")
    klucz = int(input("Twój klucz to: "))
    zaszyfrowana_wiadomosc = []

    for znak in tekst:
        indeksznaku = ZNAKI.index(znak)
        #print(f"Indeks znaku to: {indeksznaku}")
        nowy_indeks = (indeksznaku + klucz) % len(ZNAKI)
        #żeby przy bardzo dużym kluczu, lub przy zamianie znaku z końca listy nie wyjść za nią korzystamy z operatora modulo
        zaszyfrowana_wiadomosc+=ZNAKI[nowy_indeks]
        #efektem jest lista kolejnych, osobnych znaków np. ['d', 'e', 'e', 'd']
        #musimy ją połączyć, do tego służy metoda join(). W nawias wpisujemy w/w listę.
    koniec = ""    
    print(f"Twoja tajna wiadomość to: {koniec.join(zaszyfrowana_wiadomosc)}")
    
else: #odszyfrowywanie
    print("Podaj tekst, który chcesz odszyfrować.")
    tajna_wiadomosc = input("Twój szyfr: ")
    print("Teraz wybierz swój klucz, czyli o ile znaków chcesz przeskoczyć.")
    klucz_odszyfr = int(input("Twój klucz to: "))
    odszyfrowana_wiadomosc = []

    for znak in tajna_wiadomosc:
        indeks_znaku = ZNAKI.index(znak)
        stary_indeks = (indeks_znaku - klucz_odszyfr) % len(ZNAKI)
        #zamiast dodawać odejmujemy wartość klucza
        odszyfrowana_wiadomosc+=ZNAKI[stary_indeks]
    koniec = ""    
    print(f"Twoja wiadomość to: {koniec.join(odszyfrowana_wiadomosc)}")