Skip to content

Instantly share code, notes, and snippets.

@loneicewolf
Forked from amaank404/Video on Ciphers.py
Created August 23, 2024 12:21
Show Gist options
  • Save loneicewolf/7d20ef748e5211ab1cd4b7e8d0415c73 to your computer and use it in GitHub Desktop.
Save loneicewolf/7d20ef748e5211ab1cd4b7e8d0415c73 to your computer and use it in GitHub Desktop.
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