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:

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