diff options
author | Johnny Hsieh <32300164+mnixry@users.noreply.github.com> | 2023-12-29 21:56:29 +0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-12-29 10:56:29 -0300 |
commit | 91541cf726e0e9144155b6c92c5cd92f4661a4ac (patch) | |
tree | 0599bedc4230c8aadd58185481e80bb195f4aec2 | |
parent | d911018f0141358a7d752201bf1d92a9873d0a02 (diff) | |
download | pyca-cryptography-91541cf726e0e9144155b6c92c5cd92f4661a4ac.zip pyca-cryptography-91541cf726e0e9144155b6c92c5cd92f4661a4ac.tar.gz pyca-cryptography-91541cf726e0e9144155b6c92c5cd92f4661a4ac.tar.bz2 |
Add support for GCM mode of SM4 cipher (#10072)
* Add support for SM4-GCM cipher
ref: #7503
ref: https://github.com/openssl/openssl/issues/13667
* Update SM4 GCM tests to use external test vector
* Cite SM4 test vectors sources in document
* Add tests for SM4ModeGCM finalize_with_tag
* Update CHANGELOG.rst
-rw-r--r-- | CHANGELOG.rst | 4 | ||||
-rw-r--r-- | docs/development/test-vectors.rst | 3 | ||||
-rw-r--r-- | src/cryptography/hazmat/backends/openssl/backend.py | 2 | ||||
-rw-r--r-- | tests/hazmat/primitives/test_sm4.py | 92 | ||||
-rw-r--r-- | vectors/cryptography_vectors/ciphers/SM4/rfc8998.txt | 11 |
5 files changed, 109 insertions, 3 deletions
diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 9ecc48b..9ade854 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -65,6 +65,10 @@ Changelog for :class:`~cryptography.x509.Certificate` chains. These APIs should be considered unstable and not subject to our stability guarantees until documented as such in a future release. +* Added support for + :class:`~cryptography.hazmat.primitives.ciphers.algorithms.SM4` + :class:`~cryptography.hazmat.primitives.ciphers.modes.GCM` + when using OpenSSL 3.0 or greater. .. _v41-0-7: diff --git a/docs/development/test-vectors.rst b/docs/development/test-vectors.rst index a0a0261..73eaeb5 100644 --- a/docs/development/test-vectors.rst +++ b/docs/development/test-vectors.rst @@ -969,6 +969,8 @@ Symmetric ciphers * SEED (CBC) from :rfc:`4196`. * SEED (CFB, OFB) generated by this project. See: :doc:`/development/custom-vectors/seed` +* SM4 (CBC, CFB, CTR, ECB, OFB) from `draft-ribose-cfrg-sm4-10`_. +* SM4 (GCM) from :rfc:`8998`. Two factor authentication ~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -1046,6 +1048,7 @@ header format (substituting the correct information): .. _`GnuTLS example keys`: https://gitlab.com/gnutls/gnutls/-/commit/ad2061deafdd7db78fd405f9d143b0a7c579da7b .. _`NESSIE IDEA vectors`: https://www.cosic.esat.kuleuven.be/nessie/testvectors/bc/idea/Idea-128-64.verified.test-vectors .. _`NESSIE`: https://en.wikipedia.org/wiki/NESSIE +.. _`draft-ribose-cfrg-sm4-10`: https://tools.ietf.org/html/draft-ribose-cfrg-sm4-10 .. _`Ed25519 website`: https://ed25519.cr.yp.to/software.html .. _`NIST SP-800-38B`: https://csrc.nist.gov/publications/detail/sp/800-38b/archive/2005-05-01 .. _`NIST PKI Testing`: https://csrc.nist.gov/Projects/PKI-Testing diff --git a/src/cryptography/hazmat/backends/openssl/backend.py b/src/cryptography/hazmat/backends/openssl/backend.py index 58cef90..0e14bfb 100644 --- a/src/cryptography/hazmat/backends/openssl/backend.py +++ b/src/cryptography/hazmat/backends/openssl/backend.py @@ -269,7 +269,7 @@ class Backend: ), ) self.register_cipher_adapter(AES, XTS, _get_xts_cipher) - for mode_cls in [ECB, CBC, OFB, CFB, CTR]: + for mode_cls in [ECB, CBC, OFB, CFB, CTR, GCM]: self.register_cipher_adapter( SM4, mode_cls, GetCipherByName("sm4-{mode.name}") ) diff --git a/tests/hazmat/primitives/test_sm4.py b/tests/hazmat/primitives/test_sm4.py index 53893ee..695987b 100644 --- a/tests/hazmat/primitives/test_sm4.py +++ b/tests/hazmat/primitives/test_sm4.py @@ -7,9 +7,10 @@ import os import pytest -from cryptography.hazmat.primitives.ciphers import algorithms, modes +from cryptography.exceptions import InvalidTag +from cryptography.hazmat.primitives.ciphers import algorithms, base, modes -from ...utils import load_nist_vectors +from ...utils import load_nist_vectors, load_vectors_from_file from .utils import generate_encrypt_test @@ -91,3 +92,90 @@ class TestSM4ModeCTR: lambda key, **kwargs: algorithms.SM4(binascii.unhexlify(key)), lambda iv, **kwargs: modes.CTR(binascii.unhexlify(iv)), ) + + +@pytest.mark.supported( + only_if=lambda backend: backend.cipher_supported( + algorithms.SM4(b"\x00" * 16), modes.GCM(b"\x00" * 16) + ), + skip_message="Does not support SM4 GCM", +) +class TestSM4ModeGCM: + @pytest.mark.parametrize( + "vector", + load_vectors_from_file( + os.path.join("ciphers", "SM4", "rfc8998.txt"), + load_nist_vectors, + ), + ) + def test_encryption(self, vector, backend): + key = binascii.unhexlify(vector["key"]) + iv = binascii.unhexlify(vector["iv"]) + associated_data = binascii.unhexlify(vector["aad"]) + tag = binascii.unhexlify(vector["tag"]) + plaintext = binascii.unhexlify(vector["plaintext"]) + ciphertext = binascii.unhexlify(vector["ciphertext"]) + + cipher = base.Cipher(algorithms.SM4(key), modes.GCM(iv)) + encryptor = cipher.encryptor() + encryptor.authenticate_additional_data(associated_data) + computed_ct = encryptor.update(plaintext) + encryptor.finalize() + assert computed_ct == ciphertext + assert encryptor.tag == tag + + @pytest.mark.parametrize( + "vector", + load_vectors_from_file( + os.path.join("ciphers", "SM4", "rfc8998.txt"), + load_nist_vectors, + ), + ) + def test_decryption(self, vector, backend): + key = binascii.unhexlify(vector["key"]) + iv = binascii.unhexlify(vector["iv"]) + associated_data = binascii.unhexlify(vector["aad"]) + tag = binascii.unhexlify(vector["tag"]) + plaintext = binascii.unhexlify(vector["plaintext"]) + ciphertext = binascii.unhexlify(vector["ciphertext"]) + + cipher = base.Cipher(algorithms.SM4(key), modes.GCM(iv, tag)) + decryptor = cipher.decryptor() + decryptor.authenticate_additional_data(associated_data) + computed_pt = decryptor.update(ciphertext) + decryptor.finalize() + assert computed_pt == plaintext + + cipher_no_tag = base.Cipher(algorithms.SM4(key), modes.GCM(iv)) + decryptor = cipher_no_tag.decryptor() + decryptor.authenticate_additional_data(associated_data) + computed_pt = decryptor.update( + ciphertext + ) + decryptor.finalize_with_tag(tag) + assert computed_pt == plaintext + + @pytest.mark.parametrize( + "vector", + load_vectors_from_file( + os.path.join("ciphers", "SM4", "rfc8998.txt"), + load_nist_vectors, + ), + ) + def test_invalid_tag(self, vector, backend): + key = binascii.unhexlify(vector["key"]) + iv = binascii.unhexlify(vector["iv"]) + associated_data = binascii.unhexlify(vector["aad"]) + tag = binascii.unhexlify(vector["tag"]) + ciphertext = binascii.unhexlify(vector["ciphertext"]) + + cipher = base.Cipher(algorithms.SM4(key), modes.GCM(iv, tag)) + decryptor = cipher.decryptor() + decryptor.authenticate_additional_data(associated_data) + decryptor.update(ciphertext[:-1]) + with pytest.raises(InvalidTag): + decryptor.finalize() + + cipher_no_tag = base.Cipher(algorithms.SM4(key), modes.GCM(iv)) + decryptor = cipher_no_tag.decryptor() + decryptor.authenticate_additional_data(associated_data) + decryptor.update(ciphertext[:-1]) + with pytest.raises(InvalidTag): + decryptor.finalize_with_tag(tag) diff --git a/vectors/cryptography_vectors/ciphers/SM4/rfc8998.txt b/vectors/cryptography_vectors/ciphers/SM4/rfc8998.txt new file mode 100644 index 0000000..9f2e8af --- /dev/null +++ b/vectors/cryptography_vectors/ciphers/SM4/rfc8998.txt @@ -0,0 +1,11 @@ +# Vectors from rfc8998.txt. Reformatted to work with the NIST loader +# SM4 GCM + +# A.2 +COUNT = 1 +KEY = 0123456789abcdeffedcba9876543210 +IV = 00001234567800000000abcd +AAD = feedfacedeadbeeffeedfacedeadbeefabaddad2 +TAG = 83de3541e4c2b58177e065a9bf7b62ec +PLAINTEXT = aaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbccccccccccccccccddddddddddddddddeeeeeeeeeeeeeeeeffffffffffffffffeeeeeeeeeeeeeeeeaaaaaaaaaaaaaaaa +CIPHERTEXT = 17f399f08c67d5ee19d0dc9969c4bb7d5fd46fd3756489069157b282bb200735d82710ca5c22f0ccfa7cbf93d496ac15a56834cbcf98c397b4024a2691233b8d |