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 mult
(encoded_data: str)
| 62 | |
| 63 | |
| 64 | def base64_decode(encoded_data: str) -> bytes: |
| 65 | """Decodes data according to RFC4648. |
| 66 | |
| 67 | This does the reverse operation of base64_encode. |
| 68 | We first transform the encoded data back to a binary stream, take off the |
| 69 | previously appended binary digits according to the padding, at this point we |
| 70 | would have a binary stream whose length is multiple of 8, the last step is |
| 71 | to convert every 8 bits to a byte. |
| 72 | |
| 73 | >>> from base64 import b64decode |
| 74 | >>> a = "VGhpcyBwdWxsIHJlcXVlc3QgaXMgcGFydCBvZiBIYWNrdG9iZXJmZXN0MjAh" |
| 75 | >>> b = "aHR0cHM6Ly90b29scy5pZXRmLm9yZy9odG1sL3JmYzQ2NDg=" |
| 76 | >>> c = "QQ==" |
| 77 | >>> base64_decode(a) == b64decode(a) |
| 78 | True |
| 79 | >>> base64_decode(b) == b64decode(b) |
| 80 | True |
| 81 | >>> base64_decode(c) == b64decode(c) |
| 82 | True |
| 83 | >>> base64_decode("abc") |
| 84 | Traceback (most recent call last): |
| 85 | ... |
| 86 | AssertionError: Incorrect padding |
| 87 | """ |
| 88 | # Make sure encoded_data is either a string or a bytes-like object |
| 89 | if not isinstance(encoded_data, bytes) and not isinstance(encoded_data, str): |
| 90 | msg = ( |
| 91 | "argument should be a bytes-like object or ASCII string, " |
| 92 | f"not '{encoded_data.__class__.__name__}'" |
| 93 | ) |
| 94 | raise TypeError(msg) |
| 95 | |
| 96 | # In case encoded_data is a bytes-like object, make sure it contains only |
| 97 | # ASCII characters so we convert it to a string object |
| 98 | if isinstance(encoded_data, bytes): |
| 99 | try: |
| 100 | encoded_data = encoded_data.decode("utf-8") |
| 101 | except UnicodeDecodeError: |
| 102 | raise ValueError("base64 encoded data should only contain ASCII characters") |
| 103 | |
| 104 | padding = encoded_data.count("=") |
| 105 | |
| 106 | # Check if the encoded string contains non base64 characters |
| 107 | if padding: |
| 108 | assert all(char in B64_CHARSET for char in encoded_data[:-padding]), ( |
| 109 | "Invalid base64 character(s) found." |
| 110 | ) |
| 111 | else: |
| 112 | assert all(char in B64_CHARSET for char in encoded_data), ( |
| 113 | "Invalid base64 character(s) found." |
| 114 | ) |
| 115 | |
| 116 | # Check the padding |
| 117 | assert len(encoded_data) % 4 == 0 and padding < 3, "Incorrect padding" |
| 118 | |
| 119 | if padding: |
| 120 | # Remove padding if there is one |
| 121 | encoded_data = encoded_data[:-padding] |