Skip to content

Instantly share code, notes, and snippets.

@MechMK1
Created April 1, 2023 01:53
Show Gist options
  • Select an option

  • Save MechMK1/6604b61e27fced6aa9b011bf60cffd03 to your computer and use it in GitHub Desktop.

Select an option

Save MechMK1/6604b61e27fced6aa9b011bf60cffd03 to your computer and use it in GitHub Desktop.
AES-GCM: Python to JavaScript
// 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))
# 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