Created
April 1, 2023 01:53
-
-
Save MechMK1/6604b61e27fced6aa9b011bf60cffd03 to your computer and use it in GitHub Desktop.
AES-GCM: Python to JavaScript
This file contains hidden or 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
| // This function "converts" a Uint8Array to a CryptoKey object | |
| // Consider this "Boilerplate" code, that doesn't do anything useful. | |
| function importKey(rawKey) { | |
| return window.crypto.subtle.importKey("raw", rawKey, "AES-GCM", true, ["encrypt", "decrypt"]); | |
| } | |
| // This function takes raw bytes and decodes them to utf-8 | |
| function decode(bytes) { | |
| // Even though utf-8 is the default, it's always good to be explicit | |
| return new TextDecoder("utf-8").decode(bytes); | |
| } | |
| // This function takes a ciphertext, IV and key and returns the raw decrypted bytes | |
| async function decrypt(ciphertext, iv, key) { | |
| let plaintext = await crypto.subtle.decrypt({"name":"AES-GCM","iv":iv}, key, ciphertext); | |
| return plaintext; | |
| } | |
| // This is the "raw" key as byte array. You need to call importKey() to convert it to a CryptoKey object | |
| const rawKey = new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 8]) | |
| const key = await importKey(rawKey); | |
| // This converts the IV from Base64 to a Uint8Array aka byte array | |
| const iv = Uint8Array.from(atob('BtVccNu+6J1zIFjCZ6GY3w=='), c => c.charCodeAt(0)) | |
| // Same for the ciphertext | |
| const ciphertext = Uint8Array.from(atob('hZFOn9mywZCC/HNaqctHtLxX/6RFuvVY7NId3LE='), c => c.charCodeAt(0)) | |
| // This decrypts the plain bytes, then writes the decoded text to the console | |
| let plainBytes = await decrypt(ciphertext, iv, key) | |
| console.log(decode(plainBytes)) |
This file contains hidden or 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
| # base64 is not strictly necessary. It's just to transfer raw bytes from Python to JavaScript | |
| import base64 | |
| # PyCryptodrome is used to handle all the encryption and decryption. It is necessary | |
| try: | |
| from Crypto.Cipher import AES | |
| except ImportError: | |
| print("You need to install PyCryprodome") | |
| print("$ pip install pycryptodome") | |
| exit() | |
| def main(): | |
| # This represents the key [1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 8] | |
| # A valid AES key needs to be exactly 16, 24 or 32 bytes long | |
| key = b'\x01\x02\x03\x04\x05\x06\x07\x08\x01\x02\x03\x04\x05\x06\x07\x08' | |
| # This is the data to be encrypted. It needs to be in "raw byte" form. In Python, this is the "bytes" datatype. | |
| # the .encode() function is used to convert a string to a byte array | |
| # The "utf-8" parameter just explicitly states that this string was utf-8 | |
| data = "Hello, World!".encode("utf-8") | |
| # This initializes a new STATEFUL object, with the given AES key and GCM as mode. | |
| cipher = AES.new(key, AES.MODE_GCM) | |
| # This reads out the nonce / IV. | |
| # Quick side note: SubtleCrypto uses the name IV, while Pycryptodrome uses nonce | |
| # According to crypto.SE, these two are interchangable terms for GCM although nonce is "more correct". | |
| # I will still call it IV, since this is what JS calls it. | |
| iv = cipher.nonce | |
| # Since AES-GCM is authenticated encryption, you need both the ciphertext and the tag. | |
| # Pycryptodrome returns these two as separate values, but they actually belong together. | |
| # Specifically, the tag needs to be appended to the ciphertext! | |
| ciphertext, tag = cipher.encrypt_and_digest(data) | |
| # Finally, print out the IV, as well as Cipher (plus tag) | |
| cipherAndTag = ciphertext+tag | |
| print(f"IV : {base64.b64encode(iv)}") | |
| print(f"Cipher: {base64.b64encode(cipherAndTag)}, {len(cipherAndTag)}") | |
| if __name__ == '__main__': | |
| main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment