From 7db90c135d1d41b1fb2012e35b212e4eb4a898f7 Mon Sep 17 00:00:00 2001 From: 0x000c0ded Date: Mon, 12 Oct 2020 23:16:08 +0100 Subject: [PATCH 1/7] replaced base64_cipher.py with an easy to understand version --- ciphers/base64.py | 105 +++++++++++++++++++++++++++++++++++++++ ciphers/base64_cipher.py | 89 --------------------------------- 2 files changed, 105 insertions(+), 89 deletions(-) create mode 100644 ciphers/base64.py delete mode 100644 ciphers/base64_cipher.py diff --git a/ciphers/base64.py b/ciphers/base64.py new file mode 100644 index 000000000000..937d041bec96 --- /dev/null +++ b/ciphers/base64.py @@ -0,0 +1,105 @@ +B64_CHARSET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" + + +def base64_encode(data: bytes) -> str: + """Encodes data according to RFC4648. + + The data is first transformed to binary and appended with binary digits so that its + length becomes a multiple of 6, then each 6 binary digits will match a character in + the B64_CHARSET string. The number of appended binary digits would later determine + how many "=" sign should be added, the padding. + For every 2 binary digits added, a "=" sign is added in the output. + We can add any binary digits to make it a multiple of 6, for instance, consider the + following example: + "AA" -> 0010100100101001 -> 001010 010010 1001 + As can be seen above, 2 more binary digits should be added, so there's 4 + possibilities here: 00, 01, 10 or 11. + That being said, Base64 encoding can be used in Steganography to hide data in these + appended digits. + + >>> base64_encode(b"This pull request is part of Hacktoberfest20!") + 'VGhpcyBwdWxsIHJlcXVlc3QgaXMgcGFydCBvZiBIYWNrdG9iZXJmZXN0MjAh' + >>> base64_encode(b"https://tools.ietf.org/html/rfc4648") + 'aHR0cHM6Ly90b29scy5pZXRmLm9yZy9odG1sL3JmYzQ2NDg=' + >>> base64_encode(b"A") + 'QQ==' + """ + binary_stream = "".join(bin(char)[2:].zfill(8) for char in data) + + padding_needed = len(binary_stream) % 6 != 0 + + if padding_needed: + # The padding that will be added later + padding = "=" * ((6 - len(binary_stream) % 6) // 2) + + # Append binary_stream with arbitrary binary digits (0's by default) to make its + # length a multiple of 6. + binary_stream += "0" * (6 - len(binary_stream) % 6) + + # Encode every 6 binary digits to their corresponding Base64 character + encoded_data = "".join( + B64_CHARSET[int(binary_stream[index : index + 6], 2)] + for index in range(0, len(binary_stream), 6) + ) + + if padding_needed: + return encoded_data + padding + else: + return encoded_data + + +def base64_decode(encoded_data: str) -> bytes: + """Decodes data according to RFC4648. + + This does the reverse operation of base64_encode. + We first transform the encoded data back to a binary stream, take off the + previously appended binary digits according to the padding, at this point we + would have a binary stream whose length is multiple of 8, the last step is + to convert every 8 bits to a byte. + + >>> base64_decode("VGhpcyBwdWxsIHJlcXVlc3QgaXMgcGFydCBvZiBIYWNrdG9iZXJmZXN0MjAh") + b'This pull request is part of Hacktoberfest20!' + >>> base64_decode("aHR0cHM6Ly90b29scy5pZXRmLm9yZy9odG1sL3JmYzQ2NDg=") + b'https://tools.ietf.org/html/rfc4648' + >>> base64_decode("QQ==") + b'A' + """ + padding = encoded_data.count("=") + + # Check if the encoded string contains non base64 characters + if padding: + assert all( + char in B64_CHARSET for char in encoded_data[:-padding] + ), "Invalid base64 character(s) found." + else: + assert all( + char in B64_CHARSET for char in encoded_data + ), "Invalid base64 character(s) found." + + # Check the padding + assert len(encoded_data) % 4 == 0 and padding < 3, "Incorrect padding." + + if padding: + # Remove padding if there is one + encoded_data = encoded_data[:-padding] + + binary_stream = "".join( + bin(B64_CHARSET.index(char))[2:].zfill(6) for char in encoded_data + )[: -padding * 2] + else: + binary_stream = "".join( + bin(B64_CHARSET.index(char))[2:].zfill(6) for char in encoded_data + ) + + data = [ + int(binary_stream[index : index + 8], 2) + for index in range(0, len(binary_stream), 8) + ] + + return bytes(data) + + +if __name__ == "__main__": + import doctest + + doctest.testmod() diff --git a/ciphers/base64_cipher.py b/ciphers/base64_cipher.py deleted file mode 100644 index 338476934f28..000000000000 --- a/ciphers/base64_cipher.py +++ /dev/null @@ -1,89 +0,0 @@ -def encode_base64(text): - r""" - >>> encode_base64('WELCOME to base64 encoding 😁') - 'V0VMQ09NRSB0byBiYXNlNjQgZW5jb2Rpbmcg8J+YgQ==' - >>> encode_base64('AĆ…įƒš€šŸ¤“') - 'QcOF4ZCD8JCAj/CfpJM=' - >>> encode_base64('A'*60) - 'QUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFB\r\nQUFB' - """ - base64_chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" - - byte_text = bytes(text, "utf-8") # put text in bytes for unicode support - r = "" # the result - c = -len(byte_text) % 3 # the length of padding - p = "=" * c # the padding - s = byte_text + b"\x00" * c # the text to encode - - i = 0 - while i < len(s): - if i > 0 and ((i / 3 * 4) % 76) == 0: - r = r + "\r\n" # for unix newline, put "\n" - - n = (s[i] << 16) + (s[i + 1] << 8) + s[i + 2] - - n1 = (n >> 18) & 63 - n2 = (n >> 12) & 63 - n3 = (n >> 6) & 63 - n4 = n & 63 - - r += base64_chars[n1] + base64_chars[n2] + base64_chars[n3] + base64_chars[n4] - i += 3 - - return r[0 : len(r) - len(p)] + p - - -def decode_base64(text): - r""" - >>> decode_base64('V0VMQ09NRSB0byBiYXNlNjQgZW5jb2Rpbmcg8J+YgQ==') - 'WELCOME to base64 encoding 😁' - >>> decode_base64('QcOF4ZCD8JCAj/CfpJM=') - 'AĆ…įƒš€šŸ¤“' - >>> decode_base64("QUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUF" - ... "BQUFBQUFBQUFB\r\nQUFB") - 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' - """ - base64_chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" - s = "" - - for i in text: - if i in base64_chars: - s += i - c = "" - else: - if i == "=": - c += "=" - - p = "" - if c == "=": - p = "A" - else: - if c == "==": - p = "AA" - - r = b"" - s = s + p - - i = 0 - while i < len(s): - n = ( - (base64_chars.index(s[i]) << 18) - + (base64_chars.index(s[i + 1]) << 12) - + (base64_chars.index(s[i + 2]) << 6) - + base64_chars.index(s[i + 3]) - ) - - r += bytes([(n >> 16) & 255]) + bytes([(n >> 8) & 255]) + bytes([n & 255]) - - i += 4 - - return str(r[0 : len(r) - len(p)], "utf-8") - - -def main(): - print(encode_base64("WELCOME to base64 encoding 😁")) - print(decode_base64(encode_base64("WELCOME to base64 encoding 😁"))) - - -if __name__ == "__main__": - main() From 3188d3e22a5c02c409cad1b8b696600b5854ce3d Mon Sep 17 00:00:00 2001 From: 0x000c0ded Date: Mon, 12 Oct 2020 23:50:53 +0100 Subject: [PATCH 2/7] removed trailing whitespaces --- quantum/single_qubit_measure.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/quantum/single_qubit_measure.py b/quantum/single_qubit_measure.py index 99d807b034e4..7f058c2179a9 100755 --- a/quantum/single_qubit_measure.py +++ b/quantum/single_qubit_measure.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 """ -Build a simple bare-minimum quantum circuit that starts with a single -qubit (by default, in state 0), runs the experiment 1000 times, and +Build a simple bare-minimum quantum circuit that starts with a single +qubit (by default, in state 0), runs the experiment 1000 times, and finally prints the total count of the states finally observed. Qiskit Docs: https://qiskit.org/documentation/getting_started.html """ From bc5fb890440eff0e7808d86695e6613dd9e08188 Mon Sep 17 00:00:00 2001 From: hfz1337 Date: Sun, 22 Nov 2020 11:01:21 +0100 Subject: [PATCH 3/7] Rename base64.py to avoid cyclic imports when using the original base64 module --- ciphers/base64_encoding.py | 105 +++++++++++++++++++++++++++++++++++++ 1 file changed, 105 insertions(+) create mode 100644 ciphers/base64_encoding.py diff --git a/ciphers/base64_encoding.py b/ciphers/base64_encoding.py new file mode 100644 index 000000000000..937d041bec96 --- /dev/null +++ b/ciphers/base64_encoding.py @@ -0,0 +1,105 @@ +B64_CHARSET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" + + +def base64_encode(data: bytes) -> str: + """Encodes data according to RFC4648. + + The data is first transformed to binary and appended with binary digits so that its + length becomes a multiple of 6, then each 6 binary digits will match a character in + the B64_CHARSET string. The number of appended binary digits would later determine + how many "=" sign should be added, the padding. + For every 2 binary digits added, a "=" sign is added in the output. + We can add any binary digits to make it a multiple of 6, for instance, consider the + following example: + "AA" -> 0010100100101001 -> 001010 010010 1001 + As can be seen above, 2 more binary digits should be added, so there's 4 + possibilities here: 00, 01, 10 or 11. + That being said, Base64 encoding can be used in Steganography to hide data in these + appended digits. + + >>> base64_encode(b"This pull request is part of Hacktoberfest20!") + 'VGhpcyBwdWxsIHJlcXVlc3QgaXMgcGFydCBvZiBIYWNrdG9iZXJmZXN0MjAh' + >>> base64_encode(b"https://tools.ietf.org/html/rfc4648") + 'aHR0cHM6Ly90b29scy5pZXRmLm9yZy9odG1sL3JmYzQ2NDg=' + >>> base64_encode(b"A") + 'QQ==' + """ + binary_stream = "".join(bin(char)[2:].zfill(8) for char in data) + + padding_needed = len(binary_stream) % 6 != 0 + + if padding_needed: + # The padding that will be added later + padding = "=" * ((6 - len(binary_stream) % 6) // 2) + + # Append binary_stream with arbitrary binary digits (0's by default) to make its + # length a multiple of 6. + binary_stream += "0" * (6 - len(binary_stream) % 6) + + # Encode every 6 binary digits to their corresponding Base64 character + encoded_data = "".join( + B64_CHARSET[int(binary_stream[index : index + 6], 2)] + for index in range(0, len(binary_stream), 6) + ) + + if padding_needed: + return encoded_data + padding + else: + return encoded_data + + +def base64_decode(encoded_data: str) -> bytes: + """Decodes data according to RFC4648. + + This does the reverse operation of base64_encode. + We first transform the encoded data back to a binary stream, take off the + previously appended binary digits according to the padding, at this point we + would have a binary stream whose length is multiple of 8, the last step is + to convert every 8 bits to a byte. + + >>> base64_decode("VGhpcyBwdWxsIHJlcXVlc3QgaXMgcGFydCBvZiBIYWNrdG9iZXJmZXN0MjAh") + b'This pull request is part of Hacktoberfest20!' + >>> base64_decode("aHR0cHM6Ly90b29scy5pZXRmLm9yZy9odG1sL3JmYzQ2NDg=") + b'https://tools.ietf.org/html/rfc4648' + >>> base64_decode("QQ==") + b'A' + """ + padding = encoded_data.count("=") + + # Check if the encoded string contains non base64 characters + if padding: + assert all( + char in B64_CHARSET for char in encoded_data[:-padding] + ), "Invalid base64 character(s) found." + else: + assert all( + char in B64_CHARSET for char in encoded_data + ), "Invalid base64 character(s) found." + + # Check the padding + assert len(encoded_data) % 4 == 0 and padding < 3, "Incorrect padding." + + if padding: + # Remove padding if there is one + encoded_data = encoded_data[:-padding] + + binary_stream = "".join( + bin(B64_CHARSET.index(char))[2:].zfill(6) for char in encoded_data + )[: -padding * 2] + else: + binary_stream = "".join( + bin(B64_CHARSET.index(char))[2:].zfill(6) for char in encoded_data + ) + + data = [ + int(binary_stream[index : index + 8], 2) + for index in range(0, len(binary_stream), 8) + ] + + return bytes(data) + + +if __name__ == "__main__": + import doctest + + doctest.testmod() From 37c549d09eceb6174f4e438d118f6dbdbb6d9bf1 Mon Sep 17 00:00:00 2001 From: hfz1337 Date: Sun, 22 Nov 2020 11:15:28 +0100 Subject: [PATCH 4/7] compare tests results with the official implementation --- ciphers/base64_encoding.py | 39 +++++++++++++++++++++++--------------- 1 file changed, 24 insertions(+), 15 deletions(-) diff --git a/ciphers/base64_encoding.py b/ciphers/base64_encoding.py index 937d041bec96..1fb8c834c509 100644 --- a/ciphers/base64_encoding.py +++ b/ciphers/base64_encoding.py @@ -1,7 +1,10 @@ +# Import the official implementation to check if ours is correct +from base64 import b64encode, b64decode + B64_CHARSET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" -def base64_encode(data: bytes) -> str: +def base64_encode(data: bytes) -> bytes: """Encodes data according to RFC4648. The data is first transformed to binary and appended with binary digits so that its @@ -17,12 +20,15 @@ def base64_encode(data: bytes) -> str: That being said, Base64 encoding can be used in Steganography to hide data in these appended digits. - >>> base64_encode(b"This pull request is part of Hacktoberfest20!") - 'VGhpcyBwdWxsIHJlcXVlc3QgaXMgcGFydCBvZiBIYWNrdG9iZXJmZXN0MjAh' - >>> base64_encode(b"https://tools.ietf.org/html/rfc4648") - 'aHR0cHM6Ly90b29scy5pZXRmLm9yZy9odG1sL3JmYzQ2NDg=' - >>> base64_encode(b"A") - 'QQ==' + >>> a = b"This pull request is part of Hacktoberfest20!" + >>> b = b"https://tools.ietf.org/html/rfc4648" + >>> c = b"A" + >>> base64_encode(a) == b64encode(a) + True + >>> base64_encode(b) == b64encode(b) + True + >>> base64_encode(c) == b64encode(c) + True """ binary_stream = "".join(bin(char)[2:].zfill(8) for char in data) @@ -30,7 +36,7 @@ def base64_encode(data: bytes) -> str: if padding_needed: # The padding that will be added later - padding = "=" * ((6 - len(binary_stream) % 6) // 2) + padding = b"=" * ((6 - len(binary_stream) % 6) // 2) # Append binary_stream with arbitrary binary digits (0's by default) to make its # length a multiple of 6. @@ -40,7 +46,7 @@ def base64_encode(data: bytes) -> str: encoded_data = "".join( B64_CHARSET[int(binary_stream[index : index + 6], 2)] for index in range(0, len(binary_stream), 6) - ) + ).encode() if padding_needed: return encoded_data + padding @@ -57,12 +63,15 @@ def base64_decode(encoded_data: str) -> bytes: would have a binary stream whose length is multiple of 8, the last step is to convert every 8 bits to a byte. - >>> base64_decode("VGhpcyBwdWxsIHJlcXVlc3QgaXMgcGFydCBvZiBIYWNrdG9iZXJmZXN0MjAh") - b'This pull request is part of Hacktoberfest20!' - >>> base64_decode("aHR0cHM6Ly90b29scy5pZXRmLm9yZy9odG1sL3JmYzQ2NDg=") - b'https://tools.ietf.org/html/rfc4648' - >>> base64_decode("QQ==") - b'A' + >>> a = "VGhpcyBwdWxsIHJlcXVlc3QgaXMgcGFydCBvZiBIYWNrdG9iZXJmZXN0MjAh" + >>> b = "aHR0cHM6Ly90b29scy5pZXRmLm9yZy9odG1sL3JmYzQ2NDg=" + >>> c = "QQ==" + >>> base64_decode(a) == b64decode(a) + True + >>> base64_decode(b) == b64decode(b) + True + >>> base64_decode(c) == b64decode(c) + True """ padding = encoded_data.count("=") From f5220ad57e31c03bba6d51a0fefe3c224603a5b1 Mon Sep 17 00:00:00 2001 From: hfz1337 Date: Sun, 22 Nov 2020 11:20:50 +0100 Subject: [PATCH 5/7] made the suggested changes and reformatted the code with black --- ciphers/base64_encoding.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/ciphers/base64_encoding.py b/ciphers/base64_encoding.py index 1fb8c834c509..46464a76c7eb 100644 --- a/ciphers/base64_encoding.py +++ b/ciphers/base64_encoding.py @@ -41,17 +41,17 @@ def base64_encode(data: bytes) -> bytes: # Append binary_stream with arbitrary binary digits (0's by default) to make its # length a multiple of 6. binary_stream += "0" * (6 - len(binary_stream) % 6) + else: + padding = b"" # Encode every 6 binary digits to their corresponding Base64 character - encoded_data = "".join( - B64_CHARSET[int(binary_stream[index : index + 6], 2)] - for index in range(0, len(binary_stream), 6) - ).encode() - - if padding_needed: - return encoded_data + padding - else: - return encoded_data + return ( + "".join( + B64_CHARSET[int(binary_stream[index : index + 6], 2)] + for index in range(0, len(binary_stream), 6) + ).encode() + + padding + ) def base64_decode(encoded_data: str) -> bytes: From 635d2100ccb358c663e4bf0ec18f1c3ffd4e4fed Mon Sep 17 00:00:00 2001 From: hfz1337 Date: Sun, 22 Nov 2020 11:24:00 +0100 Subject: [PATCH 6/7] delete base64.py --- ciphers/base64.py | 105 ---------------------------------------------- 1 file changed, 105 deletions(-) delete mode 100644 ciphers/base64.py diff --git a/ciphers/base64.py b/ciphers/base64.py deleted file mode 100644 index 937d041bec96..000000000000 --- a/ciphers/base64.py +++ /dev/null @@ -1,105 +0,0 @@ -B64_CHARSET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" - - -def base64_encode(data: bytes) -> str: - """Encodes data according to RFC4648. - - The data is first transformed to binary and appended with binary digits so that its - length becomes a multiple of 6, then each 6 binary digits will match a character in - the B64_CHARSET string. The number of appended binary digits would later determine - how many "=" sign should be added, the padding. - For every 2 binary digits added, a "=" sign is added in the output. - We can add any binary digits to make it a multiple of 6, for instance, consider the - following example: - "AA" -> 0010100100101001 -> 001010 010010 1001 - As can be seen above, 2 more binary digits should be added, so there's 4 - possibilities here: 00, 01, 10 or 11. - That being said, Base64 encoding can be used in Steganography to hide data in these - appended digits. - - >>> base64_encode(b"This pull request is part of Hacktoberfest20!") - 'VGhpcyBwdWxsIHJlcXVlc3QgaXMgcGFydCBvZiBIYWNrdG9iZXJmZXN0MjAh' - >>> base64_encode(b"https://tools.ietf.org/html/rfc4648") - 'aHR0cHM6Ly90b29scy5pZXRmLm9yZy9odG1sL3JmYzQ2NDg=' - >>> base64_encode(b"A") - 'QQ==' - """ - binary_stream = "".join(bin(char)[2:].zfill(8) for char in data) - - padding_needed = len(binary_stream) % 6 != 0 - - if padding_needed: - # The padding that will be added later - padding = "=" * ((6 - len(binary_stream) % 6) // 2) - - # Append binary_stream with arbitrary binary digits (0's by default) to make its - # length a multiple of 6. - binary_stream += "0" * (6 - len(binary_stream) % 6) - - # Encode every 6 binary digits to their corresponding Base64 character - encoded_data = "".join( - B64_CHARSET[int(binary_stream[index : index + 6], 2)] - for index in range(0, len(binary_stream), 6) - ) - - if padding_needed: - return encoded_data + padding - else: - return encoded_data - - -def base64_decode(encoded_data: str) -> bytes: - """Decodes data according to RFC4648. - - This does the reverse operation of base64_encode. - We first transform the encoded data back to a binary stream, take off the - previously appended binary digits according to the padding, at this point we - would have a binary stream whose length is multiple of 8, the last step is - to convert every 8 bits to a byte. - - >>> base64_decode("VGhpcyBwdWxsIHJlcXVlc3QgaXMgcGFydCBvZiBIYWNrdG9iZXJmZXN0MjAh") - b'This pull request is part of Hacktoberfest20!' - >>> base64_decode("aHR0cHM6Ly90b29scy5pZXRmLm9yZy9odG1sL3JmYzQ2NDg=") - b'https://tools.ietf.org/html/rfc4648' - >>> base64_decode("QQ==") - b'A' - """ - padding = encoded_data.count("=") - - # Check if the encoded string contains non base64 characters - if padding: - assert all( - char in B64_CHARSET for char in encoded_data[:-padding] - ), "Invalid base64 character(s) found." - else: - assert all( - char in B64_CHARSET for char in encoded_data - ), "Invalid base64 character(s) found." - - # Check the padding - assert len(encoded_data) % 4 == 0 and padding < 3, "Incorrect padding." - - if padding: - # Remove padding if there is one - encoded_data = encoded_data[:-padding] - - binary_stream = "".join( - bin(B64_CHARSET.index(char))[2:].zfill(6) for char in encoded_data - )[: -padding * 2] - else: - binary_stream = "".join( - bin(B64_CHARSET.index(char))[2:].zfill(6) for char in encoded_data - ) - - data = [ - int(binary_stream[index : index + 8], 2) - for index in range(0, len(binary_stream), 8) - ] - - return bytes(data) - - -if __name__ == "__main__": - import doctest - - doctest.testmod() From b4cb4973e173e006eb5947df8f34fae0218738d9 Mon Sep 17 00:00:00 2001 From: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> Date: Sun, 22 Nov 2020 11:16:01 +0000 Subject: [PATCH 7/7] updating DIRECTORY.md --- DIRECTORY.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/DIRECTORY.md b/DIRECTORY.md index 2b3f3073c3d4..a3299c479af3 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -47,7 +47,7 @@ * [Atbash](https://github.com/TheAlgorithms/Python/blob/master/ciphers/atbash.py) * [Base16](https://github.com/TheAlgorithms/Python/blob/master/ciphers/base16.py) * [Base32](https://github.com/TheAlgorithms/Python/blob/master/ciphers/base32.py) - * [Base64 Cipher](https://github.com/TheAlgorithms/Python/blob/master/ciphers/base64_cipher.py) + * [Base64 Encoding](https://github.com/TheAlgorithms/Python/blob/master/ciphers/base64_encoding.py) * [Base85](https://github.com/TheAlgorithms/Python/blob/master/ciphers/base85.py) * [Beaufort Cipher](https://github.com/TheAlgorithms/Python/blob/master/ciphers/beaufort_cipher.py) * [Brute Force Caesar Cipher](https://github.com/TheAlgorithms/Python/blob/master/ciphers/brute_force_caesar_cipher.py) @@ -866,6 +866,7 @@ * [Covid Stats Via Xpath](https://github.com/TheAlgorithms/Python/blob/master/web_programming/covid_stats_via_xpath.py) * [Crawl Google Results](https://github.com/TheAlgorithms/Python/blob/master/web_programming/crawl_google_results.py) * [Crawl Google Scholar Citation](https://github.com/TheAlgorithms/Python/blob/master/web_programming/crawl_google_scholar_citation.py) + * [Currency Converter](https://github.com/TheAlgorithms/Python/blob/master/web_programming/currency_converter.py) * [Current Stock Price](https://github.com/TheAlgorithms/Python/blob/master/web_programming/current_stock_price.py) * [Current Weather](https://github.com/TheAlgorithms/Python/blob/master/web_programming/current_weather.py) * [Daily Horoscope](https://github.com/TheAlgorithms/Python/blob/master/web_programming/daily_horoscope.py)