Created
August 19, 2024 18:19
-
-
Save amaank404/4e9d6ef9511f1465881a75ad94b6cce1 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from manim import * | |
import string | |
import random | |
random.seed(513) | |
def gen_text(text = string.ascii_uppercase): | |
b = VGroup() | |
lr = None | |
for x in text: | |
t = Text(x).set_fill(RED) | |
r = Square(1) | |
t.scale_to_fit_height(0.7) | |
if lr: | |
r.next_to(lr, buff=0) | |
t.move_to(r.get_center()) | |
lr = r | |
c = VGroup(t, r) | |
b.add(c) | |
b.scale_to_fit_width(len(text)*14/26) | |
b.center() | |
return b | |
def ceaser_cipher(t, k): | |
s = string.ascii_uppercase | |
nt = "" | |
for char in t: | |
i = s.index(char) | |
i -= k | |
i %= len(s) | |
nt += s[i] | |
return nt | |
def interpolate(a, b, t): | |
t2 = 1-t | |
return (a[0]*t2 + b[0]*t, a[1]*t2 + b[1]*t, a[2]*t2 + b[2]*t,) | |
def quad(a, b, c, t): | |
p1 = interpolate(a, b, t) | |
p2 = interpolate(b, c, t) | |
p3 = interpolate(p1, p2, t) | |
return p3 | |
def cubic(a, b, c, d, t): | |
p1 = quad(a, b, c, t) | |
p2 = quad(b, c, d, t) | |
p3 = interpolate(p1, p2, t) | |
return p3 | |
class Scene1(Scene): | |
def construct(self): | |
""" | |
First and foremost, we have ceaser cipher, this involves in taking two strips of the ABC alphabets and a numerical key. | |
The two strips are placed one above the other and then, the strip below is shifted by the said number of places as | |
indicated by the key. Then, going over letter by letter over this mapping, we get our ciphertext. This cipher | |
is increadibly weak against both modern and old techniques, since breaking this only requires testing out all values | |
of the key. Only 26 combinations of the key are feasable since rotating past that would result in the same key. | |
The invention of this cipher is accredited to Julius Ceaser and dates back into the 1500s, this was used as a means | |
to transfer information secretly amongst armies or political associations back then. | |
""" | |
self.next_section("Intro") | |
text = Text("Ciphers", font_size=144) | |
self.play(Write(text)) | |
self.play(text.animate.scale_to_fit_height(0.5).to_edge(UP)) | |
text2 = Text("Ceaser Ciphers").move_to(text.get_center()) | |
self.play(Transform(text, text2)) | |
self.next_section("Ceaser Ciphers") | |
text2 = Text("""\ | |
Ceaser cipher is the technique of | |
transposing elements from A-Z | |
by a certain integer key forward | |
or backward""") | |
self.play(Write(text2)) | |
self.wait(4) | |
self.play(FadeOut(text2, scale=1.5)) | |
formulae = MathTex(r"E_n (x) = (x+k) \mod 26").next_to(text, DOWN) | |
self.play(Write(formulae)) | |
original_grid = gen_text().shift(UP) | |
secondary_grid = gen_text().shift(DOWN) | |
self.play(LaggedStart(Write(original_grid), Write(secondary_grid), lag_ratio=0.2)) | |
arrows = VGroup() | |
for x in range(len(string.ascii_uppercase)): | |
arrows.add(Line(start=original_grid[x].get_bottom(), end=secondary_grid[x].get_top()).add_tip(tip_length=0.2).set_opacity(0.8)) | |
self.play(Write(arrows)) | |
text2 = MathTex("k=") | |
decimal_counter = DecimalNumber(0, num_decimal_places=0).to_edge(UR) | |
text2.add_updater(lambda x: x.next_to(decimal_counter, LEFT)) | |
self.play(FadeIn(text2, decimal_counter)) | |
self.wait() | |
text_to_transform = "JASMINE" | |
text3 = MarkupText(f"Text: <span foreground='red'>{text_to_transform}</span>").next_to(secondary_grid, DOWN, buff=1) | |
self.play(FadeIn(text3)) | |
text3_right = text3.get_right() | |
text4_gen = lambda k: MarkupText(f"Text: <span foreground='{'red' if k==0 else 'green'}'>{ceaser_cipher(text_to_transform, k)}</span>").next_to(secondary_grid, DOWN, buff=1).move_to(text3_right).shift(RIGHT*2) | |
text4 = text4_gen(0) | |
self.play(text3.animate.shift(LEFT*4), FadeIn(text4)) | |
k = 0 | |
for x in range(26): | |
k += 1 | |
decimal_counter.set_value(k) | |
self.play(secondary_grid[-k].animate.next_to(secondary_grid.get_left(), LEFT, buff=0), run_time=1 if x<5 else 0.25) | |
self.play(secondary_grid.animate.shift(RIGHT*secondary_grid[-k].width), run_time=1 if x<5 else 0.25) | |
nums = [] | |
for i, char in enumerate(text_to_transform): | |
arr_index = string.ascii_uppercase.index(char) | |
nums.append(Text(str(i), font_size=20).next_to(arrows[arr_index], DOWN, buff=secondary_grid.height + 0.2)) | |
self.play(arrows[arr_index].animate.set_color(GREEN), FadeIn(nums[-1]), run_time=0.2 if x<5 else 0.05) | |
self.play(Transform(text4, text4_gen(k)), run_time=1 if x<5 else 0.25) | |
self.play(*(arrows[string.ascii_uppercase.index(char)].animate.set_color(WHITE) for char in text_to_transform), *(FadeOut(x) for x in nums), run_time=0.2 if x<5 else 0.05) | |
class Scene11(Scene): | |
def construct(self): | |
txt = VGroup(Text("Invented by Julius Ceaser", font_size=50), Text("between 100BC to 44BC", fill_opacity=0.9, font_size=30)).arrange_submobjects(DOWN).to_edge(buff=1) | |
im = ImageMobject("assets/julius.jpg").scale_to_fit_height(3).next_to(txt, RIGHT, 1) | |
self.play(Write(txt), FadeIn(im)) | |
self.wait() | |
class Scene2(Scene): | |
def construct(self): | |
""" | |
Next, we have substitution cipher, this involves in both the parties knowing a fixed sized key | |
of length 26, This key is used as a mapping to both encrypt and decrypt the information involved. | |
Here, we take the initial text jasmine, and then by meticulously going letter by letter | |
replacing it with the said key mapping, we obtain our ciphertext, which is much more secure | |
than the previous technique though still easy to crack by the modern standard using computers | |
The invention of this cipher is credited to Al'Kindi during the 9th century. | |
""" | |
self.next_section("Substitution Ciphers") | |
text = Text("Ciphers", font_size=144).scale_to_fit_height(0.5).to_edge(UP) | |
text2 = Text("Substitution Ciphers").move_to(text.get_center()) | |
self.play(Write(text2)) | |
text2 = Text("""\ | |
Substitution Cipher essentially takes a 26 | |
alphabet key and substitutes the elements | |
according to that!""") | |
self.play(Write(text2)) | |
self.wait(4) | |
self.play(FadeOut(text2, scale=1.5)) | |
key = list(string.ascii_uppercase) | |
random.shuffle(key) | |
key = ''.join(key) | |
formulae = MathTex("E_n (x) = k(x)").next_to(text, DOWN) | |
self.play(Write(formulae)) | |
original_grid = gen_text().shift(UP) | |
secondary_grid = gen_text(key).shift(DOWN) | |
text3 = Text("key", font_size=30).next_to(secondary_grid, DOWN, aligned_edge=LEFT, buff=0.5) | |
self.play(LaggedStart(Write(original_grid), Write(secondary_grid), Write(text3), lag_ratio=0.2)) | |
arrows = VGroup() | |
for x in range(len(string.ascii_uppercase)): | |
arrows.add(Line(start=original_grid[x].get_bottom(), end=secondary_grid[x].get_top()).add_tip(tip_length=0.2).set_opacity(0.8)) | |
self.play(Write(arrows)) | |
t = "JASMINE" | |
t2 = "" | |
for x in t: | |
t2 += key[string.ascii_uppercase.index(x)] | |
text4 = Text(t, color=RED).to_corner(DL, buff=1) | |
self.play(Write(text4), run_time=0.5) | |
for i, char in enumerate(t): | |
arr_index = string.ascii_uppercase.index(char) | |
self.play(arrows[arr_index].animate.set_color(GREEN), FadeIn(Text(str(i), font_size=20).next_to(arrows[arr_index], DOWN, buff=secondary_grid.height + 0.2)), run_time=0.5) | |
text5 = Text(t2, color=GREEN).to_corner(DR, buff=1) | |
arrow = Line(text4.get_right()+RIGHT, text5.get_left()+LEFT).add_tip() | |
self.play(Write(text5), Create(arrow), run_time=1) | |
self.wait(3) | |
class Scene21(Scene): | |
def construct(self): | |
txt = VGroup(Text("Invented by Al'Kindi", font_size=50), Text("between 801AD to 873AD", fill_opacity=0.9, font_size=30)).arrange_submobjects(DOWN).to_edge(buff=1) | |
im = ImageMobject("assets/alkindi.jpg").scale_to_fit_height(3).next_to(txt, RIGHT, 1) | |
self.play(Write(txt), FadeIn(im)) | |
self.wait() | |
class IntroScene(Scene): | |
def construct(self): | |
"""When we talk, chat, or browse the internet, we come across many bad actors, aka, people really interested in your personal information. | |
To mitigate this, some smart people dating as far back as 50BC invented cryptography and ciphers to encrypt your messages and to transfer | |
them securely over any medium, from the paper back in the 1500s, now to the electronic transfer of data via Internet and Radio, everything | |
involves cryptography. Today, we'll look into some of the first cryptographic functions with the help of animations!""" | |
im = SVGMobject("assets/inet.svg").scale_to_fit_height(6).center().set_fill(WHITE) | |
self.play(Write(im), run_time=4) | |
self.wait(0.4) | |
self.play(im.animate.scale(0.5).to_edge(LEFT, 1)) | |
diagram = VGroup( | |
SVGMobject("assets/man-person-icon.svg").set_fill(WHITE).scale_to_fit_height(1), | |
Line().set_length(3).add_tip(), | |
SVGMobject("assets/man-person-icon.svg").set_fill(WHITE).scale_to_fit_height(1), | |
).arrange_submobjects(RIGHT).shift(RIGHT) | |
self.play(Write(diagram), run_time=3) | |
self.play(diagram.animate.to_edge(RIGHT, 1)) | |
hacker_im = SVGMobject("assets/crime-hacker-icon.svg").scale_to_fit_height(2.5).center().set_fill(WHITE).shift(LEFT*0.8) | |
self.play(Write(hacker_im)) | |
self.play(hacker_im.animate.scale_to_fit_height(1), run_time=0.5) | |
self.play(MoveAlongPath(hacker_im, ParametricFunction(lambda t: cubic(hacker_im.get_center(), hacker_im.get_center()+DOWN*2, (1, -2, 0), (5, -2, 0), t)))) | |
unsafe_icon = SVGMobject("assets/unsafe-icon.svg").scale_to_fit_height(0.7).move_to(diagram[1]) | |
txt = Text("Unencrypted Communication", slant=ITALIC, font_size=30).next_to(diagram, DOWN).shift(RIGHT*0.2) | |
self.play(FadeOut(diagram[2]), hacker_im.animate.move_to(diagram[2].get_center()).shift(RIGHT*0.2), Write(unsafe_icon), Write(txt)) | |
self.wait() | |
self.play(*(FadeOut(x) for x in self.mobjects)) | |
txt = Text("Ciphers", font_size=144) | |
self.play(Write(txt)) | |
container_rect = RoundedRectangle(0.5, height = 2.5, width = 8, fill_color=WHITE, fill_opacity=1).next_to(txt, DOWN, buff=-0.75) | |
paper = SVGMobject("assets/paper.svg").scale_to_fit_height(2).next_to(txt, DOWN, buff=-0.5) | |
ineticon = SVGMobject("assets/ineticon.svg").scale_to_fit_height(2).next_to(txt, DOWN, buff=-0.5) | |
self.play(FadeIn(container_rect), Write(paper), txt.animate.shift(UP)) | |
self.play(paper.animate.to_edge(LEFT, 4)) | |
self.play(Write(ineticon)) | |
self.play(ineticon.animate.to_edge(RIGHT, 4)) | |
contained_items = VGroup(container_rect, paper, ineticon) | |
self.play(FadeOut(contained_items, scale=1.5), txt.animate.center()) | |
self.wait(5) | |
class Scene3(Scene): | |
""" | |
Next we shall be looking over a cipher that stood the test of time | |
for a whole 3 centuries before it was uncovered! Named after Blaise | |
de Vigenere, though it was initially discovered by Saint-Pourçain-sur-Sioule. | |
This cipher is essentially a more complex variant of ceaser cipher | |
where each letter in the alphabet is assigned numbers 1 through 26. | |
Then, the given key is repeated up until the length of the input text | |
then, iterating over this, the key's corresponding index to the text's | |
corresponding index is taken. The key's numerical value is then used | |
as input to ceaser's cipher and a resultant ciphertext value comes for | |
the said text character! | |
Though this method is pretty strong to be breakable by hand, it | |
still is cryptographically weak against the modern standard such as | |
AES-256. | |
That being said, it was discovered in the 16th century! | |
""" | |
def construct(self): | |
self.next_section("Vigenère Ciphers") | |
text = Text("Ciphers", font_size=144).scale_to_fit_height(0.5).to_edge(UP) | |
text2 = Text("Vigenère Ciphers").move_to(text.get_center()) | |
self.play(Write(text2)) | |
text2 = Text("""\ | |
Vigenère Cipher essentially takes a n-length | |
alphabet key and using the n-th element's numeric | |
value in the alphabet performs a ceaser cipher | |
with the said numeric value for the n-th letter of | |
the input.""").scale_to_fit_width(14) | |
self.play(Write(text2)) | |
self.wait(4) | |
self.play(FadeOut(text2, scale=1.5)) | |
key = list(string.ascii_uppercase) | |
random.shuffle(key) | |
key = ''.join(key) | |
formulae = MathTex(r"E_k (X_i) = (X_i + K_i) \mod 26").next_to(text, DOWN) | |
self.play(Write(formulae)) | |
t = "JASMINE" | |
orig_grid = gen_text("JASMINE").arrange_submobjects(DOWN, buff=0).center().to_edge(LEFT).shift(DOWN*1) | |
for i, x in enumerate(orig_grid): | |
num = Text(str(i+1), font_size=20) | |
num.next_to(x, RIGHT, buff=0.1) | |
x.add(num) | |
key_txt = Text("key").scale_to_fit_width(orig_grid.width).next_to(orig_grid, UP) | |
self.play(Write(orig_grid), Write(key_txt)) | |
cipherwheelout = gen_text(string.ascii_uppercase).scale_to_fit_width(10) | |
cipherwheelin = gen_text(string.ascii_uppercase).next_to(cipherwheelout, DOWN).scale_to_fit_width(10) | |
wheelheights = cipherwheelout.height*1.5 | |
self.play(LaggedStart(Write(cipherwheelin), Write(cipherwheelout))) | |
self.play(cipherwheelin.animate.apply_function( | |
rotate_point(cipherwheelin, 3-wheelheights, 1.5) | |
).move_to(ORIGIN+DOWN), | |
cipherwheelout.animate.apply_function( | |
rotate_point(cipherwheelout, 3, 1.5) | |
).move_to(ORIGIN+DOWN), run_time=5) | |
cipherwheelgroup = VGroup(cipherwheelout) | |
self.remove(cipherwheelin) | |
theta = ValueTracker(0) | |
cipherwheelin_updated = always_redraw(lambda: cipherwheelin.copy().rotate(theta.get_value())) | |
self.add(cipherwheelin_updated) | |
center_dot = Dot(cipherwheelgroup.get_center(), 0.1) | |
line1 = Line(start=cipherwheelgroup.get_center(), end=cipherwheelin.get_top()-np.array([0, wheelheights, 0]), color=RED) | |
line2 = always_redraw(lambda: Line(start=cipherwheelgroup.get_center(), end=cipherwheelin.get_top()-np.array([0, wheelheights, 0]), color=GREEN).rotate(theta.get_value(), about_point=cipherwheelgroup.get_center())) | |
key_grp = VGroup( | |
MathTex("key ="), (current_key := DecimalNumber(num_decimal_places=0)) | |
).arrange_submobjects(RIGHT).scale_to_fit_width(cipherwheelin.get_top()[1] - wheelheights).next_to(center_dot, RIGHT) | |
cipherwheelgroup.add(line1, center_dot, line2, cipherwheelin_updated, key_grp) | |
self.play(Create(center_dot), Write(line1), Write(line2), Write(key_grp)) | |
kd = -2*PI/26 | |
current_key.add_updater(lambda t: t.set_value(theta.get_value()//kd)) | |
text_to_encrypt = "SOMESUPERBTEXT" | |
encrypted_text = [" "]*len(text_to_encrypt) | |
txt = gen_text(text_to_encrypt).arrange_submobjects(DOWN, 0).scale_to_fit_height(7).next_to(np.array([cipherwheelgroup.get_right()[0], 0, 0]), RIGHT, 2) | |
def __t(): | |
return gen_text("".join(encrypted_text)).arrange_submobjects(DOWN, 0).scale_to_fit_height(7).next_to(txt, RIGHT) | |
txt2 = __t() | |
self.play(LaggedStart(Write(txt), Write(txt2), lag_ratio=0.2)) | |
arrow = Line().set_length(1).add_tip(tip_length=0.1, tip_width=0.1).rotate(PI) | |
arrow.next_to(orig_grid[0]) | |
arrow2 = Line().set_length(1).add_tip(tip_length=0.1, tip_width=0.1) | |
arrow2.next_to(txt[0], LEFT) | |
self.play(Write(arrow), Write(arrow2)) | |
kp = 0 # Key Position | |
tp = 0 # Text Position | |
for x in text_to_encrypt: | |
_k = string.ascii_uppercase.index(t[kp]) | |
_t = string.ascii_uppercase.index(x) | |
encrypted_text[tp] = string.ascii_uppercase[(_t - _k)%26] | |
self.play(theta.animate.set_value(_k*kd), arrow.animate.next_to(orig_grid[kp]), arrow2.animate.next_to(txt[tp], LEFT), run_time=1) | |
self.play(Transform(txt2, __t())) | |
kp += 1 | |
kp %= len(t) | |
tp += 1 | |
self.play(theta.animate.set_value(0)) | |
class Scene31(Scene): | |
def construct(self): | |
txt = VGroup(Text("Invented by Saint-Pourçain-sur-Sioule", font_size=50), Text("between 1523AD to 1596AD", fill_opacity=0.9, font_size=30), Text("Falsely accredited to Blaise de Vigenère", fill_opacity=0.9, font_size=30)).arrange_submobjects(DOWN).to_edge(buff=1).scale_to_fit_width(8) | |
im = ImageMobject("assets/vigenere.png").scale_to_fit_height(3).next_to(txt, RIGHT, 0.7) | |
self.play(Write(txt), FadeIn(im)) | |
self.wait() | |
def rotate_point(mobj: VMobject, init_r=2, scale=1): | |
t_w = mobj.width | |
x_pos = mobj.get_left()[0] | |
y_pos = mobj.get_top()[1] | |
def rot(p): | |
r = (p[1] - y_pos)*scale + init_r | |
theta = (-(p[0] - x_pos)*2*PI/t_w) + (PI/2) | |
x = r*np.cos(theta) + x_pos | |
y = r*np.sin(theta) + y_pos*scale - init_r | |
return np.array([x, y, p[2]]) | |
return rot | |
class Outro(Scene): | |
""" | |
That would be all for now, today, we learnt about the ceaser's cipher, the substitution cipher | |
and the vigenere cipher. This video was programmatically created using Manim Library and Python. | |
Looking forward to a victory, now tuning off, Amaan from Team Jasmine! | |
""" | |
def construct(self): | |
banner = ManimBanner() | |
self.play(banner.create()) | |
self.play(banner.expand()) | |
self.wait() | |
self.play(Unwrite(banner)) | |
pylogo = ImageMobject("assets/python-3.png").scale_to_fit_width(12) | |
self.play(FadeIn(pylogo)) | |
self.wait() | |
self.play(FadeOut(pylogo)) | |
t = VGroup( | |
Text("Thank You", font_size=140), | |
Text("By Amaan Khan, 12-A", font_size=60, slant=ITALIC), | |
Text("Jasmine", color=RED) | |
).arrange_submobjects(DOWN) | |
self.play(Write(t), run_time=2) | |
self.wait() | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Woah. respect. This is really great. 🎖️