Add AES wrappers and fix tests
diff --git a/python/amcl/aes.py b/python/amcl/aes.py
new file mode 100644
index 0000000..ab28514
--- /dev/null
+++ b/python/amcl/aes.py
@@ -0,0 +1,144 @@
+"""
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+"""
+
+"""
+
+This module use cffi to access the c functions for the amcl AES-GCM.
+
+"""
+
+from . import core_utils
+import platform
+
+_ffi = core_utils._ffi
+_ffi.cdef("""
+extern void AES_GCM_ENCRYPT(octet *K,octet *IV,octet *H,octet *P,octet *C,octet *T);
+extern void AES_GCM_DECRYPT(octet *K,octet *IV,octet *H,octet *C,octet *P,octet *T);
+""")
+
+if (platform.system() == 'Windows'):
+    _libamcl_core = _ffi.dlopen("libamcl_core.dll")
+elif (platform.system() == 'Darwin'):
+    _libamcl_core = _ffi.dlopen("libamcl_core.dylib")
+else:
+    _libamcl_core = _ffi.dlopen("libamcl_core.so")
+
+
+# Constants
+AES_KEY = 32 # Length in bytes of an AES key
+
+
+def gcm_encrypt(aes_key, iv, header, plaintext):
+    """AES-GCM Encryption
+
+    AES-GCM Encryption
+
+    Args::
+
+        aes_key: AES Key
+        iv: Initialization vector
+        header: header
+        plaintext: Plaintext to be encrypted
+
+    Returns::
+
+        ciphertext: resultant ciphertext
+        tag: MAC
+
+
+    Raises:
+
+    """
+    aes_key1, aes_key1_val = core_utils.make_octet(None, aes_key)
+    iv1, iv1_val = core_utils.make_octet(None, iv)
+    header1, header1_val = core_utils.make_octet(None, header)
+    plaintext1, plaintext1_val = core_utils.make_octet(None, plaintext)
+    tag1, tag1_val = core_utils.make_octet(AES_KEY)
+    ciphertext1, ciphertext1_val = core_utils.make_octet(len(plaintext))
+    _ = aes_key1_val, iv1_val, header1_val, plaintext1_val, tag1_val, ciphertext1_val # Suppress warnings
+
+    _libamcl_core.AES_GCM_ENCRYPT(
+        aes_key1,
+        iv1,
+        header1,
+        plaintext1,
+        ciphertext1,
+        tag1)
+    tag = core_utils.to_str(tag1)
+    ciphertext = core_utils.to_str(ciphertext1)
+
+    # clear memory
+    core_utils.clear_octet(aes_key1)
+    core_utils.clear_octet(iv1)
+    core_utils.clear_octet(header1)
+    core_utils.clear_octet(plaintext1)
+    core_utils.clear_octet(tag1)
+    core_utils.clear_octet(ciphertext1)
+
+    return ciphertext, tag
+
+
+def gcm_decrypt(aes_key, iv, header, ciphertext):
+    """AES-GCM Decryption
+
+    AES-GCM Deryption
+
+    Args::
+
+        aes_key: AES Key
+        iv: Initialization vector
+        header: header
+        ciphertext: ciphertext
+
+    Returns::
+
+        plaintext: resultant plaintext
+        tag: MAC
+
+    Raises:
+
+    """
+    aes_key1, aes_key1_val = core_utils.make_octet(None, aes_key)
+    iv1, iv1_val = core_utils.make_octet(None, iv)
+    header1, header1_val = core_utils.make_octet(None, header)
+    ciphertext1, ciphertext1_val = core_utils.make_octet(None, ciphertext)
+    tag1, tag1_val = core_utils.make_octet(AES_KEY)
+    plaintext1, plaintext1_val = core_utils.make_octet(len(ciphertext))
+    _ = aes_key1_val, iv1_val, header1_val, plaintext1_val, tag1_val, ciphertext1_val # Suppress warnings
+
+    _libamcl_core.AES_GCM_DECRYPT(
+        aes_key1,
+        iv1,
+        header1,
+        ciphertext1,
+        plaintext1,
+        tag1)
+
+    tag = core_utils.to_str(tag1)
+    plaintext = core_utils.to_str(plaintext1)
+
+    # clear memory
+    core_utils.clear_octet(aes_key1)
+    core_utils.clear_octet(iv1)
+    core_utils.clear_octet(header1)
+    core_utils.clear_octet(plaintext1)
+    core_utils.clear_octet(tag1)
+    core_utils.clear_octet(ciphertext1)
+
+    return plaintext, tag
diff --git a/python/benchmark/bench_aes.py b/python/benchmark/bench_aes.py
new file mode 100755
index 0000000..bc7611b
--- /dev/null
+++ b/python/benchmark/bench_aes.py
@@ -0,0 +1,51 @@
+#!/usr/bin/env python3
+
+"""
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+"""
+
+import os
+import sys
+from bench import time_func
+
+sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))
+
+from amcl import core_utils, aes
+
+key_hex    = "2768a4f5a75344fee0ed46faaf7b020111fe5f0e80a88c0fd27abfcc15bc9d68"
+header_hex = "1554a69ecbf04e507eb6985a234613246206c85f8af73e61ab6e2382a26f457d"
+iv_hex     = "2b213af6b0edf6972bf996fb"
+
+if __name__ == "__main__":
+    key    = bytes.fromhex(key_hex)
+    header = bytes.fromhex(header_hex)
+    iv     = bytes.fromhex(iv_hex)
+    
+    plaintext = b'test message'
+    
+    # Generate quantities for bench run
+    ciphertext, tag = aes.gcm_encrypt(key, iv, header, plaintext)
+    dec_plaintext, dec_tag = aes.gcm_decrypt(key, iv, header, ciphertext)
+    assert tag == dec_tag, 'Inconsistent decryption tag'
+    
+    # Run benchmark
+    fncall = lambda: aes.gcm_encrypt(key, iv, header, plaintext)
+    time_func("aes.gcm_encrypt", fncall, unit = 'us')
+
+    fncall = lambda: aes.gcm_decrypt(key, iv, header, ciphertext)
+    time_func("aes.gcm_decrypt", fncall, unit = 'us')
diff --git a/python/examples/example_aes.py b/python/examples/example_aes.py
new file mode 100755
index 0000000..7f02fdf
--- /dev/null
+++ b/python/examples/example_aes.py
@@ -0,0 +1,52 @@
+#!/usr/bin/env python3
+
+"""
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+"""
+
+import os
+import sys
+
+sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))
+
+from amcl import core_utils, aes
+
+key_hex    = "2768a4f5a75344fee0ed46faaf7b020111fe5f0e80a88c0fd27abfcc15bc9d68"
+header_hex = "1554a69ecbf04e507eb6985a234613246206c85f8af73e61ab6e2382a26f457d"
+iv_hex     = "2b213af6b0edf6972bf996fb"
+
+if __name__ == "__main__":
+    key    = bytes.fromhex(key_hex)
+    header = bytes.fromhex(header_hex)
+    iv     = bytes.fromhex(iv_hex)
+    
+    plaintext = b'test message'
+    print(f"Encrypt message '{plaintext.decode('utf-8')}'")
+    print(f'\th  = {header.hex()}')
+    print(f'\tiv = {iv.hex()}')
+    
+    ciphertext, tag = aes.gcm_encrypt(key, iv, header, plaintext)
+    print("\nEncrypted message")
+    print(f'ct  = {ciphertext.hex()}')
+    print(f'tag = {tag.hex()}')
+
+    print("\nDecrypt message")
+    dec_plaintext, dec_tag = aes.gcm_decrypt(key, iv, header, ciphertext)
+    assert tag == dec_tag, '\tInconsistent decryption tag'
+    
+    print(f"\tgot '{dec_plaintext.decode('utf-8')}'")
diff --git a/python/test/CMakeLists.txt b/python/test/CMakeLists.txt
index 4290634..bdc05c3 100644
--- a/python/test/CMakeLists.txt
+++ b/python/test/CMakeLists.txt
@@ -53,7 +53,12 @@
 file(GLOB SCHNORR_TV "${PROJECT_SOURCE_DIR}/testVectors/factoring_zk/*.json")
 file(COPY ${SCHNORR_TV} DESTINATION "${PROJECT_BINARY_DIR}/python/test/factoring_zk/")
 
+# GCM test vectors
+file(GLOB GCM_TV "${PROJECT_SOURCE_DIR}/testVectors/gcm/*.json")
+file(COPY ${GCM_TV} DESTINATION "${PROJECT_BINARY_DIR}/python/test/gcm/")
+
 if(NOT CMAKE_BUILD_TYPE STREQUAL "ASan")
+  add_python_test(test_python_aes              test_aes.py)
   add_python_test(test_python_mpc_mta          test_mta.py)
   add_python_test(test_python_mpc_r            test_r.py)
   add_python_test(test_python_mpc_s            test_s.py)
diff --git a/python/test/test_aes.py b/python/test/test_aes.py
new file mode 100755
index 0000000..7a2b0d1
--- /dev/null
+++ b/python/test/test_aes.py
@@ -0,0 +1,97 @@
+#!/usr/bin/env python3
+
+"""
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+"""
+
+import os
+import sys
+import json
+import unittest
+
+sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))
+
+from amcl import core_utils, aes
+
+
+class TestEncrypt(unittest.TestCase):
+    """ Test AES-GCM encryption """
+
+    def setUp(self):
+        with open("gcm/encrypt.json", "r") as f:
+            self.tv = json.load(f)
+
+        for vector in self.tv:
+            for key, val in vector.items():
+                vector[key] = bytes.fromhex(val)
+
+    def test_tv(self):
+        """ Test using test vectors """
+
+        for vector in self.tv:
+            tag_golden = vector['tag']
+
+            ct, tag = aes.gcm_encrypt(
+              vector['key'],
+              vector['iv'],
+              vector['aad'],
+              vector['pt'])
+
+            # Cut tag to match length of test vector tag
+            tag = tag[:len(tag_golden)]
+
+            self.assertEqual(ct,  vector['ct'])
+            self.assertEqual(tag, tag_golden)
+
+
+class TestDecrypt(unittest.TestCase):
+    """ Test AES-GCM decryption """
+
+    def setUp(self):
+        with open("gcm/decrypt.json", "r") as f:
+            self.tv = json.load(f)
+
+        for vector in self.tv:
+            for key, val in vector.items():
+                if key != "fail":
+                    vector[key] = bytes.fromhex(val)
+
+    def test_tv(self):
+        """ Test using test vectors """
+
+        for vector in self.tv:
+            tag_golden = vector['tag']
+
+            pt, tag = aes.gcm_decrypt(
+              vector['key'],
+              vector['iv'],
+              vector['aad'],
+              vector['ct'])
+
+            # Cut tag to match length of test vector tag
+            tag = tag[:len(tag_golden)]
+
+            if vector.get('fail', False):
+                self.assertNotEqual(tag, tag_golden)
+            else:
+                self.assertEqual(pt,  vector['pt'])
+                self.assertEqual(tag, tag_golden)
+
+if __name__ == '__main__':
+    # Run tests
+    unittest.main()
diff --git a/python/test/test_nm_commit.py b/python/test/test_nm_commit.py
index a317a79..e7dfb7a 100755
--- a/python/test/test_nm_commit.py
+++ b/python/test/test_nm_commit.py
@@ -22,7 +22,7 @@
 import os
 import sys
 import json
-from unittest import TestCase
+import unittest
 
 sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))
 
@@ -38,7 +38,7 @@
             vector[key] = bytes.fromhex(val)
 
 
-class TestNMCommit(TestCase):
+class TestNMCommit(unittest.TestCase):
     """ Test NM Commitment Commit """
 
     def setUp(self):
@@ -71,7 +71,7 @@
         self.assertEqual(c, self.c_golden)
 
 
-class TestNMDecommit(TestCase):
+class TestNMDecommit(unittest.TestCase):
     """ Test NM Commitment Decommit """
 
     def test_tv(self):
@@ -88,3 +88,7 @@
         rc = commitments.nm_decommit(vector['X'], vector['X'], vector['C'])
 
         self.assertEqual(rc, commitments.FAIL)
+
+if __name__ == '__main__':
+    # Run tests
+    unittest.main()
diff --git a/python/test/test_schnorr.py b/python/test/test_schnorr.py
index 7b2e561..766c37f 100755
--- a/python/test/test_schnorr.py
+++ b/python/test/test_schnorr.py
@@ -144,6 +144,5 @@
 
         self.assertEqual(ec, schnorr.FAIL)
 
-
 if __name__ == '__main__':
     unittest.main()
diff --git a/python/test/test_zk_factoring.py b/python/test/test_zk_factoring.py
index e8ad703..7218de5 100755
--- a/python/test/test_zk_factoring.py
+++ b/python/test/test_zk_factoring.py
@@ -22,7 +22,7 @@
 import os
 import sys
 import json
-from unittest import TestCase
+import unittest
 
 sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))
 
@@ -37,7 +37,7 @@
 y_hex = "b4ebebd6177b2eb04149aa463ede7ba2216657e3b4de42f496c0d493b4d734131e63edcde042d951b9bf285622b9d69e9ee170156deeb173725032a952068e68b18f69bd4e52677d48d846055988877ce9e97b962f01e3f425f3101a6a589f020c858b1ee5ae8f79e4c63ce2356d8a9aa703100b3b3588d0aae7d7857b672d1beb25afc90a93045837aca1c39511816d4fc84ad0db35edf9adac810c46965868e79a5eb9509f9d7c315c5439daf561b312c0dd276263464409aef75a65c157277ba0bcef2cb1929995ba6749a8c54187cf2a9cfc9febc40bee8b149973590f9d34ae8c79111792e92b5fcdbd993f6ce8ad1558f5f8e691c3ce2ca9b2c15f599c"
 
 
-class TestProve(TestCase):
+class TestProve(unittest.TestCase):
     """ Test ZK factoring Prove """
 
     def setUp(self):
@@ -75,7 +75,7 @@
         self.assertEqual(e, self.e)
         self.assertEqual(y, self.y)
 
-class TestVerify(TestCase):
+class TestVerify(unittest.TestCase):
     """ Test ZK factoring Verify """
 
     def setUp(self):
@@ -101,3 +101,7 @@
         ec = factoring_zk.verify(self.tv[0]['Y'], self.tv[0]['E'], self.tv[0]['N'])
 
         self.assertEqual(ec, factoring_zk.FAIL)
+
+if __name__ == '__main__':
+    # Run tests
+    unittest.main()
diff --git a/testVectors/gcm/decrypt.json b/testVectors/gcm/decrypt.json
new file mode 100644
index 0000000..c78c68a
--- /dev/null
+++ b/testVectors/gcm/decrypt.json
@@ -0,0 +1,122 @@
+[
+    {
+        "key": "f333b0c686bcbb1d03d590dbcdbc578baa1a1ce48f4d4c25699e9c62cbfb396c",
+        "iv": "7b7b9a7c4c9a78604e44e738",
+        "ct": "6a11bef495744d53d39266217fa286c901d917a336d9865b615543d3abf756555bfc476e8a611ec696b65f0db084c5c2148d17",
+        "aad": "3fee34cbdd62d5e91a872321c15e7e5a39a12733227facaf6df3b8dda4f0c4e384df6440646d3511c8ca409fe6ea4e415f18e4784363f30be04bc2695dd0bfb7751771e1266bd5e6b5c8b46a0e30b3520ab2f35900114d5ec769",
+        "tag": "81bde5f797c99e592d829e270d1e7b",
+        "fail": true
+    },
+    {
+        "key": "42e89f5a310b01c2b5e672732724bf88e7c662143f7083f13f6c9fa224b2e65a",
+        "iv": "5c8507e7668778dc2ecab087",
+        "ct": "ba59ca6a40f403ec1bc5688aa4b6410389d9cb44c492038b456a39cd948f52d4843599e0fd0cfbb86ce9a082f34c3cb93d71d5",
+        "aad": "b6b3aa35f24a8a3c71aa4cb05f78626080f6f610a1dbbb2dc1f990cefa6e7d7517e134272c0d64350716a7b28746fb824a0fce9d5e9c7225108ab101bacbf10ad690a2416f4895b57c2059812bb154c9e00b79b988a97de7a5c9",
+        "tag": "7ae7151ecbde44600e2244d1c3ae91",
+        "pt": "2351e99dc3253b3fb3ff2dc7e03568c7342fc47aa2b4d80499ba897d92ca7903efecf879d250685a1b64bcbd274b2e5a79d345"
+    },
+    {
+        "key": "ee46bd7f1532396eb65abdc203de7935e6de8ef8e8d697193a4774ebd24cf006",
+        "iv": "d3f7e1ab82c2883959961051",
+        "ct": "7a82b7e3ec2f5ca8cac33a6a5f5c870331b33c2ca98c29c746413d6512c88b0ef404be7634a4de9b07932b05f26f8056783bb8",
+        "aad": "655e12440e142549f359cbe29bec88044191633218e949713ea4863f6399e4a0d22c91e679d0ea60bd8bdb555fe9f4f2eb77aa73e9980844a90536930505156dbd4cbb5106d34a6314ba19a16c788bc1b3c2676ae09b9068a537",
+        "tag": "bf13c1530cfc679bc2de1577b5ea90",
+        "fail": true
+    },
+    {
+        "key": "1706b0d251afa33339d38033be4a33edb34ead8790497f73a1f0c1f01c4ae808",
+        "iv": "186dba4dadecff91b571f5a9",
+        "ct": "0488a0e5fdf51480600d7ca64dcd4ecbdd7f3ca74b6a0e71cbaff79775b54b1887836304c3c63fc59489271cbef62f8ec53727",
+        "aad": "48f072e73e47c70b9e48264a9350334ff5a5c72cf5d977ab254b2410a94639bc687e96bf944641f174fe7c573ac9abebee0f030b2ca9b7e56aea336a15a77853e8b5d00b2202bb3318e092956ae4e4d89097c7fe58743ac39349",
+        "tag": "1fca31e24f56e9f43b26e46fb52127",
+        "pt": "264b3ae74c80151e433463095a34e3ad580af91d7934049dfe084b037b2457d93a81326f6cf4c144b7afb3f97892ebccf43aa5"
+    },
+    {
+        "key": "ad5b35c873e6f7d9f1b7b52bd01bdd2bfc572951661b0d2c41b6cf189d033e5b",
+        "iv": "707df6109f18a729a491d75b",
+        "ct": "18b3dd32a004bedac36a6871f592eb00aa3ca92ef2ef41418ba63d3bb7bad0c903232e6f97bf917fa35fdf982fbb51add7bef2",
+        "aad": "d50d26de9277dbed86dbb802ad1fa2605f87df1733efbfa027e8ead5a35ed352e17a69dce4b72955e65904ec04019400bcd2e6b01ca0a462242617728bf119a3a1b78b954923d513845abfde94f51035a555fc03abf222b2496c",
+        "tag": "1a1a116d4d208dc1bee821e13a0960",
+        "fail": true
+    },
+    {
+        "key": "294a411ab2246643f2535c067b7883364312e66bd14cf898904936c82ad1b598",
+        "iv": "f132a415306e79f1f2834297",
+        "ct": "3b490217f23ec3318ffef05f00e48ad779e0e2d92f83524b3e834f7935da2537dc5db44547fa2c326d1f92579b151bb9cfe7a8",
+        "aad": "1e29ad9d1bbe15581630e309130a558565dc5e2992ca552143225cf628a3549a725b1b2a6298cc1c6a7a508a99c2c8bb7f069648ba8ebb48b878d8b7a81d5196e282007fdf6968f11e79092392e5dd810eaf0e33a1b104fb0ec9",
+        "tag": "d8114bf8ec87b7c47df402ae5dff9a",
+        "pt": "a8cc2bbb7f91fad53a1bf58e3e4302ec903ea6c2c52850069719ba1ed738253f8c7aecd0e1ac534d2d61baced6290a20078583"
+    },
+    {
+        "key": "3ebf3367f317602af4daa731e53b68a5afec44004752220a2e9ce70bc9c52e9f",
+        "iv": "3df8277e31df3f14661f1d25",
+        "ct": "b9ca360b140a081048ef7166da6eb2c2bdb1687b7680d623ef7cb198c1d2909d5088ff18aafc3154fa80a26b6b01f20145d681",
+        "aad": "6291944f6af0d2e7d157c45f0644e938867dc130b73f3642f94a2dc3dea4bd9066b5890ecb3a743c33bf799a076182dec531f36b4b57a69be75efe810108044e9a3b9ff7be2bdb7bcfb8f089002e1b5dc4b366063ca167d8b994",
+        "tag": "e53905a8ef86ebeaa572ce54e736a9",
+        "fail": true
+    },
+    {
+        "key": "6f01e19e6824cbb1609416d363c51363a9972400c35b0762f8ee6aba02d25658",
+        "iv": "2454f4ad282dd5147a882568",
+        "ct": "42ea352c0865e6a69abc7cca71e4654b1b5718f10672a24bceef2fb68949215d1288378109154bd09042527cb104d1882eed88",
+        "aad": "3f9bb88b565676104ccae289d8aad2f2c4977549e349f08dd871d79f4053453902a8317e614fbdd27d5a78fe90cbd1def2b3973a8e5f42d94fc03b13d0da04348b156c8b47b1039e0f34d07200e49c6e7782c9d73abd961cb9e4",
+        "tag": "db84e4c27b727a7f51079f25a045b9",
+        "fail": true
+    },
+    {
+        "key": "55be6101473c13347d310a2eb5a9b7acd238b19e23172166bb8dc4fd20a562e9",
+        "iv": "f8e52cb8aca018a202e03f66",
+        "ct": "559b6448f361ba06229ca19c673c767e861267739bfdf136d1434aa4a92b4c5dc92b7e6264909f10512a1a31058216c99b7564",
+        "aad": "5b8e91619f651909e8c59451301cd81ab8cb8041eec694186d6adc6eac74420c8f909249cd4b2f669d418751f76e55722eabb374ae7fa2aa444b7d0870acb182231d46063a3f92ef254a9fa016d3fe9f656f28fb890a5b17565f",
+        "tag": "56fffb1d28ca0eb79c495de282ad29",
+        "pt": "090088bafa81194c6e32dbdf07bf95aa41ee0cd7cece158c73764b5083931999139b52beed8a1792901bce025cc14118dbae99"
+    },
+    {
+        "key": "9a3985d1aac6c7442081d501fec6c67cc8ddd88532304144bc559de1db2d0590",
+        "iv": "70378f1ac5cda3065f79509d",
+        "ct": "e6debbe01162911ce3bb27a11f320586e876ce98f355e0974155db154d16c2560b2c4461677d094c3a06aa027efd39e2ec8f28",
+        "aad": "bab1be91a56b63c472cecb9944de6b181276a1a05821098fa23ac9e3becbe995b3e6ef1729152fd2042063010a93035171da71f5426a9819e165048822b4ee208d2dd0f365f0167933b09c63a34d152ad6d75439e9a90d853431",
+        "tag": "928058202c1cd1e5fa98e2b7813416",
+        "fail": true
+    },
+    {
+        "key": "2e54f798857493570af754efd0504cd9968268a242c451ad53118362dd205598",
+        "iv": "d4c8be5e236869e31dba803e",
+        "ct": "9dc03a16043281f53093b3c61e5ecfcbbb8d9a288a0e87a8d6e9ca3edb46a1ac5d0d89b6ca2208b9ae60107fe2e7596a59be04",
+        "aad": "d15f175a3b6fceb4c34e6de92b8bb0103ce8246e3861a0f8450842a70520e6db87c8067a1a68c7a38a931ae993728fcc2a3d4667fe493461167aaf3f1e700d59ba4a4061dd1043c9a41a7f7496ef38b4a495d441f4c2b179cab4",
+        "tag": "7d49eab4fd339853ad89ae130b3348",
+        "fail": true
+    },
+    {
+        "key": "13076348b9573a368bb4d0f78fdc869999b5c983903aeb3e27e9ffae26758295",
+        "iv": "1dbb4924aa5e35833fe626e0",
+        "ct": "683bb6615c7849e15bb788baa0bbf53fdd7a050fc82d39c757b9493a99354612e1eb9eb36366858112f5277db37ffad60db646",
+        "aad": "d2d939368fdbfe1e1028706a23160b53ebd2f5193acee85dbb85c12e79998c473132b6946992ce5e8f18e1c225fc01b0e41236a8af887b32b752ae4cd3d9fc166890d2dff5a1ecea6ba2fd6e3da5d5b9414fb426d24fba2e35dd",
+        "tag": "a2c284fa8ca43f9657ab19313e078f",
+        "pt": "83e93ac98dd6be17c7d34e4ba05cc03bbd725af332c080dbc4fa7c5024f250559fe793ff575f4d397aeda68e19a01e88330151"
+    },
+    {
+        "key": "b9f7fe61f962c5cd6ab1dbcb731124a267962260cd7dba869ae543d0824f235c",
+        "iv": "e74b85d128664152d03b840c",
+        "ct": "59e4bf0112930f04bcf201955164d215e31a9119ecc3cfe45c02f0c379a266144d59d694b3faa9989878b245b17a4cc3dc7415",
+        "aad": "c7eb3430964f75220dba229d944304b034bcf6155e3cdd262e8ced974b9ef47d65952c9fe8c6b8aa19518eddb931f944a7eb854636e95e1d57b28f802b1818c7ac1c46f1401a35cc40ff21ee1fa3d604f55ad742e855d08f9aaa",
+        "tag": "5ecf5208fa54c651e016977d922234",
+        "fail": true
+    },
+    {
+        "key": "7b2fb7482ee903de64b9926a2521535f11790a2bbfa5b63a7aeca0e47abe4736",
+        "iv": "0c537af58a945691734bb68e",
+        "ct": "2646306a001d57624974c49878bf65d2d622d4e5e8b04e956ba27ae9ac97421929f6f04b69c52afb70a7fe7a415e552dcddab0",
+        "aad": "48a7b5bf90184c7f009d5ebb5e3add7553a19d2cac3fd61f578ddd9b262690d9735b91848003eb7894bc387e018e70ff0365db3e77cd89552a07a3aa0ef696971f5ca2af3bdac7596ac1dcdc6e6328ed32c312e5b3ec7844eb4b",
+        "tag": "bc1fe26bfe2b49b9c042075bbf40b6",
+        "fail": true
+    },
+    {
+        "key": "c542cd02344dd8197d840b77b1111de549e6a8a36a017ff6cd9222bf9a77d50b",
+        "iv": "1490a4c51507ccd80c2cdd2c",
+        "ct": "331c5e94fee5809fa4dcd80e964ef6b34711be5f2535afec2bd2dc0f819b63d5acacff9d6c130a0efc935dc53c8735f60dee55",
+        "aad": "362d384617e06fd04a8e3ee22c15c73cda3d7776be9e31cbf437b9ed0dfdade26fee87ee6d645f675bea155cc300fa8f1094f7b56e60a8fcce24258b97f8f7e567eae59544822fbe457d5bd04987267da91836da678cbc40b8e7",
+        "tag": "9aa61b27a8a2c1149354f0eac3d5ff",
+        "fail": true
+    }
+]
\ No newline at end of file
diff --git a/testVectors/gcm/encrypt.json b/testVectors/gcm/encrypt.json
new file mode 100644
index 0000000..e15fdad
--- /dev/null
+++ b/testVectors/gcm/encrypt.json
@@ -0,0 +1,122 @@
+[
+    {
+        "key": "02c1ab503e738646350f7bd07f79f6262a851e1674ae8bfe50503ff14b903be7",
+        "iv": "aa",
+        "pt": "ab44dcd10a56f7c7573601e486f6e4a46f183b61c0baca3eb50858fb528e3ad6",
+        "aad": "f5e1595da0cc02ebfbc6e3680cdf6db2b64fd1ce",
+        "ct": "e5f688511ff7f51e51f09ea4ddc860f82de2097b26604f581bfe495d403e7670",
+        "tag": "e4bce1c56cb18ee522bd179163fe5c"
+    },
+    {
+        "key": "a4240cb4398a50db55655a601516a12405970dae725c1ca81d7b773bd1c617e6",
+        "iv": "84",
+        "pt": "9be4ebefbec6142e7cfa582b129719bfaf456547c059a61afc8c44ae6ff00fa9",
+        "aad": "e7254c7abcd4611e634a2141e3ab9236397a50c5",
+        "ct": "f3c27b43498b429f29d21e39200744d38c122bc4157bc32c3f6456d96fa7f1a7",
+        "tag": "273d1c6bb76b914d6cf61d374841cc"
+    },
+    {
+        "key": "2cb417cc7cdf02633c55b48b83a87e5e92668d684055cc40e2b65e8e0d089f3b",
+        "iv": "5a",
+        "pt": "f99f1ad143285005b99ee3d6aae90e7c959aae281945161b80a9744395f99897",
+        "aad": "0b4fbc84566d5037388094b9fac2df1c2a6a06f6",
+        "ct": "03c4b947cb7237e403698233cfe9971cb4581208a24cc005beb865469615c5c3",
+        "tag": "3c1d95e2f9455e556f780e4ad7b437"
+    },
+    {
+        "key": "bf83f817efdc7325dcfbab1680d044cd78e4ffd97c86776e873172f1919aa4df",
+        "iv": "90",
+        "pt": "174a1df629f50234fdd168d4284f5805970b7e41a723627f67a6de8f7f2ed6f1",
+        "aad": "0c278198d1a16194846adb753d3a9613537203ff",
+        "ct": "2499586025958f44f2a73f6aa96231e359ebf785c529c8f30092452658a688ea",
+        "tag": "c7be9fb82371a512aad1f95717f407"
+    },
+    {
+        "key": "450d9be135fe52c8bc1c7f7b99263587cefcc195640edb032fed61741401d513",
+        "iv": "28",
+        "pt": "9937b8a6b3aa6c5b6f859fd80d7b3dd5d8029eb6c1ef6b76f1c052656fdb791f",
+        "aad": "ce18c60639800f277e5cdf82576c8e67d06b6873",
+        "ct": "692ab0c9224ccc95f7af2d41d9d4f06c424abcf442bf9d7024efc13cb3c3e3d6",
+        "tag": "c694f26aa76251190b0fb7de8e0399"
+    },
+    {
+        "key": "76886e586046c82975cfb43f345e98d3c476e4f2e35c363206d0f8f85baec8e2",
+        "iv": "be",
+        "pt": "1125a65f181b8f60279e671209d934fd285eadfb4f88d43bdcba2ce2f3e46e72",
+        "aad": "bfd48018f4238930d2f1f8694321aa997678776b",
+        "ct": "3638d1e21545c2ede1121880b0605ae0fe4871070eaed0e9ac84e34389d32a25",
+        "tag": "f1790ad27763ff80bc8395ce521a56"
+    },
+    {
+        "key": "6052da11e4a12baddbd36ad6ab867f8bab912f60f1325ee0503c965533dd9612",
+        "iv": "09",
+        "pt": "d46c9900d1a80038fff6fa7c8c888dab8609466151155a56c1b802b35ed2c70b",
+        "aad": "46592d234a499ff277917f1094c6e8ba6a87562c",
+        "ct": "2f814b9fa8ce70976f4a7fd96fe5b47924b142c9fa92536ea8fc09e6b8cb9e1b",
+        "tag": "70a1b596b91e621984a3aeffccd267"
+    },
+    {
+        "key": "b40847160ad630c780625bd524574cbf6bd423c078bc7bdbdb010b21adee688d",
+        "iv": "36",
+        "pt": "cc7017c408963a8ccd456feedbc753f646349078c428a4cf236ee78faf29d7b6",
+        "aad": "526530113587d3eb7c780e68cf6c12e9ec350d3a",
+        "ct": "4230c114dd6270b9be86447bdcd88daec027162f4eacf2f625d56d492cdd63ce",
+        "tag": "f4340e8077e68a9b1fd234553c62bb"
+    },
+    {
+        "key": "2830b7a67bdd1ba1b8f6230ea0bf15d3e55a9ecfcb34b1a1b429d52a2176f771",
+        "iv": "a4",
+        "pt": "4867a3acac0a3fdc1929bd6abf9877216c22d707ddaa21b33054654071e0fa53",
+        "aad": "31ae8e0860b1981f7da30ef6bfe58902fb439c76",
+        "ct": "e12052e3e9c0950e0d50f954cdbfe053da8509b0ad562525053d9105f303722c",
+        "tag": "63d80d9ce6b9075df5c6f8c2973a88"
+    },
+    {
+        "key": "23b7d10ff9c90ebc8bbf1e9452043b7283d3e3067af67754aecfdbcb1d497bc7",
+        "iv": "79",
+        "pt": "b408146ce3a217177ae52ebdf08bfe368efd6cf9ba991fd4f34362128580033f",
+        "aad": "00e3210784ade07253f13d93e5572a97a0d2645c",
+        "ct": "34b2630b0c59294e39805ae246655990eba3d61982abb3c759e78e33af5002e4",
+        "tag": "4f205ab52d23d07cd21836f31d30cb"
+    },
+    {
+        "key": "681b0943d04e157dcd3bbe235f7eb5d5105213dd32af8649c3ed471f964ad199",
+        "iv": "13",
+        "pt": "8d460e62eee55b8f21586e88c2c66a7febe5d7ba605fe026fc388a8dad1fa22b",
+        "aad": "11739983b9e6012207202b01214421df5b93a2af",
+        "ct": "1d932f8a19a6362d0fba589e53798a12ac0731d95ed42508fb9d45b895af01a7",
+        "tag": "35aa5cef546b55c127fce5d8691706"
+    },
+    {
+        "key": "038c8ccc281355c2d53901b369ab1601e7bbbcddaf80f369d3f98602566c4534",
+        "iv": "a5",
+        "pt": "6bd288fbf078585b217d12ada7f6099eb6bc888767c9ecb5f853d0dafdb3c976",
+        "aad": "0dd59a54bb264d5fbf9239bbac09d5f2ed4f7b09",
+        "ct": "4a50a6583e6e056b14ba3003a8b7e56e147df1f72029055dfbceb5d418bff6f0",
+        "tag": "dcfe2e07affd7c611ad50f72e15cb6"
+    },
+    {
+        "key": "91d5b6d229f0ba6425f93edd9c97017a77938f825f924f23f502ec779b7d6cdc",
+        "iv": "1a",
+        "pt": "da40251ec590f5281a4f1fc44b16e1318c1e7827638942517f329b369b3e8bb5",
+        "aad": "69756ecf8959059af0e734f0abdc1bcb6a5642d9",
+        "ct": "8fee67a2c4c6e11d38b2c30eae8004c75a3da0df5dac5f9b01d385380cd36a47",
+        "tag": "406ee50e1f0fc002c5979305c37ed6"
+    },
+    {
+        "key": "36adeb5b3147f47016725337120f21e96967563a4212ea3334db75adb43f5311",
+        "iv": "a5",
+        "pt": "a9de15e59ef9a24ec003f3bd90b0312b41eb17e6bbc72343ce232cd665eefc83",
+        "aad": "05059d6232ee4d22a926ca34e5d027b33bef5121",
+        "ct": "6a38f5c24c267b7faa376660588a56ef940961b1900dab814744be7e78135212",
+        "tag": "3022c99a2b6391afe4c01f9f23c815"
+    },
+    {
+        "key": "b5d556d9a9b813178c79381e0ffec2451008fa88ec58d044c62ec2a701975c8d",
+        "iv": "c4",
+        "pt": "eea8e96764044314fe2ffd56f56321f2e18d0d34bd4343240bad613e26665aab",
+        "aad": "e5134db8f48fd27a6517b1ec4bf99ac33df2870c",
+        "ct": "20ca89d7978d8433d3d29db6f97efe393f9b49abcd93e50ffb21bae47b741ddc",
+        "tag": "7d4e01356a4c4c0c0edad1f3867999"
+    }
+]
\ No newline at end of file