Merge pull request #2097 from alex/test-iface

Fixed #1689 -- correctly handle code with multiple requires_backend_itnerface
diff --git a/docs/development/test-vectors.rst b/docs/development/test-vectors.rst
index ca07547..fe64fe1 100644
--- a/docs/development/test-vectors.rst
+++ b/docs/development/test-vectors.rst
@@ -215,6 +215,9 @@
 * ``nc_invalid_ip_netmask.pem`` - An RSA 2048 bit self-signed certificate
   containing a name constraints extension with a permitted element that has an
   ``IPv6`` IP and an invalid network mask.
+* ``nc_single_ip_netmask.pem`` - An RSA 2048 bit self-signed certificate
+  containing a name constraints extension with a permitted element that has two
+  IPs with ``/32`` and ``/128`` network masks.
 * ``cp_user_notice_with_notice_reference.pem`` - An RSA 2048 bit self-signed
   certificate containing a certificate policies extension with a
   notice reference in the user notice.
diff --git a/src/cryptography/hazmat/primitives/ciphers/base.py b/src/cryptography/hazmat/primitives/ciphers/base.py
index 8f3028f..dae9365 100644
--- a/src/cryptography/hazmat/primitives/ciphers/base.py
+++ b/src/cryptography/hazmat/primitives/ciphers/base.py
@@ -149,6 +149,8 @@
 class _AEADCipherContext(object):
     def __init__(self, ctx):
         self._ctx = ctx
+        self._bytes_processed = 0
+        self._aad_bytes_processed = 0
         self._tag = None
         self._updated = False
 
@@ -156,6 +158,14 @@
         if self._ctx is None:
             raise AlreadyFinalized("Context was already finalized.")
         self._updated = True
+        self._bytes_processed += len(data)
+        if self._bytes_processed > self._ctx._mode._MAX_ENCRYPTED_BYTES:
+            raise ValueError(
+                "{0} has a maximum encrypted byte limit of {1}".format(
+                    self._ctx._mode.name, self._ctx._mode._MAX_ENCRYPTED_BYTES
+                )
+            )
+
         return self._ctx.update(data)
 
     def finalize(self):
@@ -171,6 +181,15 @@
             raise AlreadyFinalized("Context was already finalized.")
         if self._updated:
             raise AlreadyUpdated("Update has been called on this context.")
+
+        self._aad_bytes_processed += len(data)
+        if self._aad_bytes_processed > self._ctx._mode._MAX_AAD_BYTES:
+            raise ValueError(
+                "{0} has a maximum AAD byte limit of {0}".format(
+                    self._ctx._mode.name, self._ctx._mode._MAX_AAD_BYTES
+                )
+            )
+
         self._ctx.authenticate_additional_data(data)
 
 
diff --git a/src/cryptography/hazmat/primitives/ciphers/modes.py b/src/cryptography/hazmat/primitives/ciphers/modes.py
index e31c906..4284042 100644
--- a/src/cryptography/hazmat/primitives/ciphers/modes.py
+++ b/src/cryptography/hazmat/primitives/ciphers/modes.py
@@ -139,6 +139,8 @@
 @utils.register_interface(ModeWithAuthenticationTag)
 class GCM(object):
     name = "GCM"
+    _MAX_ENCRYPTED_BYTES = (2 ** 39 - 256) // 8
+    _MAX_AAD_BYTES = (2 ** 64) // 8
 
     def __init__(self, initialization_vector, tag=None, min_tag_length=16):
         # len(initialization_vector) must in [1, 2 ** 64), but it's impossible
diff --git a/tests/hazmat/primitives/test_aes.py b/tests/hazmat/primitives/test_aes.py
index 4d48e8a..2c3e5f9 100644
--- a/tests/hazmat/primitives/test_aes.py
+++ b/tests/hazmat/primitives/test_aes.py
@@ -253,3 +253,53 @@
         computed_ct = encryptor.update(pt) + encryptor.finalize()
         assert computed_ct == ct
         assert encryptor.tag == tag
+
+    def test_gcm_ciphertext_limit(self, backend):
+        encryptor = base.Cipher(
+            algorithms.AES(b"\x00" * 16),
+            modes.GCM(b"\x01" * 16),
+            backend=backend
+        ).encryptor()
+        encryptor._bytes_processed = modes.GCM._MAX_ENCRYPTED_BYTES - 16
+        encryptor.update(b"0" * 16)
+        assert (
+            encryptor._bytes_processed == modes.GCM._MAX_ENCRYPTED_BYTES
+        )
+        with pytest.raises(ValueError):
+            encryptor.update(b"0")
+
+    def test_gcm_aad_limit(self, backend):
+        encryptor = base.Cipher(
+            algorithms.AES(b"\x00" * 16),
+            modes.GCM(b"\x01" * 16),
+            backend=backend
+        ).encryptor()
+        encryptor._aad_bytes_processed = modes.GCM._MAX_AAD_BYTES - 16
+        encryptor.authenticate_additional_data(b"0" * 16)
+        assert encryptor._aad_bytes_processed == modes.GCM._MAX_AAD_BYTES
+        with pytest.raises(ValueError):
+            encryptor.authenticate_additional_data(b"0")
+
+    def test_gcm_ciphertext_increments(self, backend):
+        encryptor = base.Cipher(
+            algorithms.AES(b"\x00" * 16),
+            modes.GCM(b"\x01" * 16),
+            backend=backend
+        ).encryptor()
+        encryptor.update(b"0" * 8)
+        assert encryptor._bytes_processed == 8
+        encryptor.update(b"0" * 7)
+        assert encryptor._bytes_processed == 15
+        encryptor.update(b"0" * 18)
+        assert encryptor._bytes_processed == 33
+
+    def test_gcm_aad_increments(self, backend):
+        encryptor = base.Cipher(
+            algorithms.AES(b"\x00" * 16),
+            modes.GCM(b"\x01" * 16),
+            backend=backend
+        ).encryptor()
+        encryptor.authenticate_additional_data(b"0" * 8)
+        assert encryptor._aad_bytes_processed == 8
+        encryptor.authenticate_additional_data(b"0" * 18)
+        assert encryptor._aad_bytes_processed == 26
diff --git a/vectors/cryptography_vectors/x509/custom/nc_single_ip_netmask.pem b/vectors/cryptography_vectors/x509/custom/nc_single_ip_netmask.pem
new file mode 100644
index 0000000..2931b6b
--- /dev/null
+++ b/vectors/cryptography_vectors/x509/custom/nc_single_ip_netmask.pem
@@ -0,0 +1,19 @@
+-----BEGIN CERTIFICATE-----
+MIIC/TCCAeWgAwIBAgITBnA4pkis5m3OGusBaihd9qH0hzANBgkqhkiG9w0BAQsF
+ADAXMRUwEwYDVQQDDAxjcnlwdG9ncmFwaHkwHhcNMTUwNzAxMjAxNDAwWhcNMTYw
+NjMwMjAxNDAwWjAXMRUwEwYDVQQDDAxjcnlwdG9ncmFwaHkwggEiMA0GCSqGSIb3
+DQEBAQUAA4IBDwAwggEKAoIBAQCYyaGtu90vcm+jN+SoQHXxWMQyplY1neL9KjfE
++TsKKcy8TKJEqlT8qZr6bIL3KVbTIiYO8bCW9fHSMgHWrmtr37LlFoQ3emcLfDbM
+kybmOolAxA78im0L2BIW1wT2iSHh1p/ZO5QLdt+e8zP5AkZAnXCZk912RcJYyGUW
+7JQzzRfEANSLE9Gmh78NsxWNI1Ipc3dhyuk3+YHwePGCzLCeXCiF4FHGNMg8Drtr
+rENNHZjHJCbMLfK9irHV5Xh1FHTK8xlqEq+YecpqboUyqgWVOOvpxUxiKagfp//Z
++iFDC1+GgpuupzFUiHPSVCZGMnE3bHvIBOkoHkNu7kNK7VX3AgMBAAGjQjBAMD4G
+A1UdHgEB/wQ0MDKgMDAihyAA/wAAAAAAAAAAAAAAAAAA////////////////////
+/zAKhwjAqAAB/////zANBgkqhkiG9w0BAQsFAAOCAQEAXSDmonnBpivsW/NKE85c
+1ho449K98+1cFUD51VeK42oPUd0GRQCU3ETYJ5YyK7OMoQqe4LTtNDx6ZCF+6z/r
+tZctfdpwRmqh2ebGn3qDs1FAckkwwSCRtkJTdgznmtO680Ls5GveNFrgYJkYfFjj
+OWpzCypse/3j3uVgSakmjBRS4BOsyX4o7trN+k1MmQOrMpWEtLlmrZJpM66sgP0j
+WpI95l4paIMpkFarwCCQfJCNBpl7Uol+BD4vJvf/J7f7ZwxQMEWCBPnYk3EKMnKa
+aCsmqRMV1W7SzxL07dHMzWnsC/I5oJNj4HdthGcIJf1Jut9A9KFVodOJAxKOziz2
+aQ==
+-----END CERTIFICATE-----