0

I am working on converting the encryption function of AES/GCM-256 in C# to Python. i found the following code in Python and i am using it, but the problem is that my data in the Python function is the same as the C# data. The output string (encoded) produced by my Python function is not the same as the output of the C# function, although my inputs (key, iv and data) are the same in both functions.

I would be grateful if someone could help me

My keyis: b'4fda3c622e966e0839441401bbd3b8f191d4267bf5f19b40812a34b212fd3ed9'

My iv is: b'4fda3c622e966e0839441401bbd3b8f191d4267bf5f19b40812a34b212fd3ed9'

C# function

        public static string AesEncrypt(byte[] payload, byte[] key, byte[] iv)
        {
            var cipher = new GcmBlockCipher(new AesEngine());

            byte[] baPayload = new byte[0];
            cipher.Init(true, new AeadParameters(new KeyParameter(key), 128, iv, baPayload));
            var cipherBytes = new byte[cipher.GetOutputSize(payload.Length)];
            int len = cipher.ProcessBytes(payload, 0, payload.Length, cipherBytes, 0);
            cipher.DoFinal(cipherBytes, len);
            return Convert.ToBase64String(cipherBytes);
        }

It can be converted to bytes using the following function PASSPHRASE in C#:

 public static byte[] StringToByteArray(string hex)
        {
            return Enumerable.Range(0, hex.Length)
                            .Where(x => x % 2 == 0)
                            .Select(x => Convert.ToByte(hex.Substring(x, 2), 16))
                            .ToArray();
        }

python:

PASSPHRASE= b'a4b42ed2702cb1b00a14f39a88c719cb04e5e8b29e2479634c990258e327483'

        def AES_encrypt(data,iv):
           global PASSPHRASE
           data_json_64 = data
           key = binascii.unhexlify(PASSPHRASE)
           cipher = AES.new(key, AES.MODE_GCM, iv)
           x = cipher.encrypt(data)
           return x

Because all my data is the same, I expect my output to be the same, but it is not

My input test string to both C# and Python is:: "In publishing and graphic design, Lorem ipsum is a"

My iv to both C# and Python is: '4fda3c622e966e0839441401bbd3b8f191d4267bf5f19b40812a34b212fd3ed9'

The encoded output in C# is: 02Em9Vve6fWtAcVNesIXzagoB327EmskwMZdRippAAaxqAzkp0VeGSjctbaguqA/01CnPHB2PkRDDOxjgZ9pAfu2

The encoded output in Python is: HudpKzIov7lNt4UNng+a9P/FLXrzdenwDBT4uFYhIUc3XOS7TpaCzxja8I+zHCdXnvk=

Amalendu
  • 1
  • 1
  • 3
    How do you convert passphrase to `byte[]` in C#? – Aleksa Majkic Jan 31 '23 at 15:28
  • Your implementation of GCM with PyCryptodome is incomplete, you need `encrypt_and_digest()`, see the examples in the PyCryptodome [GCM documentation](https://pycryptodome.readthedocs.io/en/latest/src/cipher/modern.html?highlight=gcm#gcm-mode). Also, the keys differ in both codes and in the Python code the test IV is unclear. Post complete test data for both codes. Note that the recommended IV/nonce size for GCM is 12 bytes. – Topaco Jan 31 '23 at 16:06
  • @Topaco Thank you for taking the time for me You are right, as you say and it is also in the documentation, I should use `encrypt_and_digest()` but when I use it, the length of my encrypted string is much longer. – Amalendu Feb 01 '23 at 03:51
  • @Topaco If the same test data is entered for both functions, the output will be different, it doesn't matter what the data is, for example `"In publishing and graphic design, Lorem ipsum is a"` – Amalendu Feb 01 '23 at 03:53
  • @Topaco The value of iv is the same as it is in the text of the message for both C# and Python functions, even the key value of all values is the same. `iv=b'4fda3c622e966e0839441401bbd3b8f191d4267bf5f19b40812a34b212fd3ed9'` – Amalendu Feb 01 '23 at 03:54
  • @AleksaMajkic Thank you for taking the time for me, I think I have answered your question by editing my question – Amalendu Feb 01 '23 at 04:02
  • The C# code implicitly concatenates ciphertext and tag in the order: `ciphertext|tag`. `encrypt_and_digest()`, however, returns ciphertext and tag individually. For comparison with the C# code, both must be explicitly concatenated. – Topaco Feb 01 '23 at 04:39
  • Given the same data (plaintext, key and IV/nonce), the concatenated data of both codes should be identical. If this is not the case, post test data for both codes (i.e. plaintext and ciphertext for the posted key and IV/nonce). For clarity, please not in comments, but in the question. – Topaco Feb 01 '23 at 04:40
  • @Topaco I don't include a value for the `nonce`, should I do that? – Amalendu Feb 01 '23 at 04:49
  • In the context of GCM, the IV is also often referred to as a nonce (which is why I wrote IV/nonce). – Topaco Feb 01 '23 at 04:51
  • I can reproduce the posted result of the C# code, but not that of the Python code. I get the same result with the Python code as with the C# code when the suggested changes are made. You can see and run my Python code online here: https://replit.com/@3hK8cL8H24hwiS7/SturdyPowerfulMinimalsystem#main.py. Compare your Python code and mine to find the bug in yours. – Topaco Feb 01 '23 at 08:17

1 Answers1

0

After implementing suggested changes from your comments and some minor tweaks to your code, it is possible to reproduce your C# result. The main problem was the handle of a ciphertext and a tag. As @Topaco has stated in the comments, C# implicitly concatenates ciphertext and tag as ciphertext|tag, which will mean you need to do the same in your code. If you follow the GCM documentation you will see that you need to use encrypt_and_digest() function in order to obtain the ciphertext and the tag. Here is what you need to change:

  • cipher = AES.new(key, AES.MODE_GCM, iv) cipher = AES.new(key, AES.MODE_GCM, binascii.unhexlify(iv))
  • x = cipher.encrypt(data) x, key = cipher.encrypt_and_digest(data)
  • return x return x + key

These changes should help you fix your code. I also have some other suggestions. I wouldn't use global variables in your code. For starters, this will mean that your function should be AES_encrypt(data, key, iv) and not AES_encrypt(data, iv). Why are global variables a bad idea? In this particular case it makes no sense you use a global variable for password as you shouldn't use the same password for encryption of everything. Additionally, when talking about encryption, you will see that every algorithm is using a key which is not the same as a password. Here you are implementing the password based encryption. However, you are not doing it correctly, as you are using a password (passphrase) as a cryptographic key. The idea behind a password based encryption is to use a good digest/key stretching technique to create a key from your password. Finally, you should take a look at the NIST Special Publication 800-38D to learn more about the GCM and NIST's suggested parameters when it comes to implementing a safe encryption.

Aleksa Majkic
  • 632
  • 5
  • 17