diff --git a/src/electionguard/big_integer.py b/src/electionguard/big_integer.py
index efacaa5..80f72e9 100644
--- a/src/electionguard/big_integer.py
+++ b/src/electionguard/big_integer.py
@@ -9,7 +9,9 @@ from .utils import BYTE_ORDER
 
 def _hex_to_int(input: str) -> int:
     """Given a hex string representing bytes, returns an int."""
-    return int(input, 16)
+    valid_bytes = input[1:] if (len(input) % 2 != 0 and input[0] == "0") else input
+    hex_bytes = bytes.fromhex(valid_bytes)
+    return int.from_bytes(hex_bytes, BYTE_ORDER)
 
 
 def _int_to_hex(input: int) -> str:
@@ -33,8 +35,8 @@ _zero = mpz(0)
 def _convert_to_element(data: Union[int, str]) -> Tuple[str, int]:
     """Convert element to consistent types"""
     if isinstance(data, str):
-        hex = data
         integer = _hex_to_int(data)
+        hex = _int_to_hex(integer)
     else:
         hex = _int_to_hex(data)
         integer = data

diff --git a/tests/property/test_hash.py b/tests/property/test_hash.py
index 899dc79..540648e 100644
--- a/tests/property/test_hash.py
+++ b/tests/property/test_hash.py
@@ -1,10 +1,13 @@
 from typing import List, Optional
 
 from hypothesis import given
+from hypothesis.strategies import integers
+
 
 from tests.base_test_case import BaseTestCase
 
-from electionguard.group import ElementModQ
+from electionguard.big_integer import BigInteger
+from electionguard.group import ElementModP, ElementModQ
 from electionguard.hash import hash_elems
 from electionguard_tools.strategies.group import elements_mod_p, elements_mod_q
 
@@ -28,6 +31,43 @@ class TestHash(BaseTestCase):
         if ha != hb:
             self.assertNotEqual(a, b)
 
+    @given(elements_mod_p(), integers(min_value=0, max_value=10))
+    def test_hash_of_big_integer_with_leading_zero_bytes(
+        self, input: ElementModP, multiplier: int
+    ) -> None:
+        """Test hashing of larger integers with leading zero bytes"""
+
+        # Arrange.
+        zero_byte = "00"
+        input_hash = hash_elems(input)
+        leading_zeroes = zero_byte * multiplier + input.to_hex()
+
+        # Act.
+        leading_zeroes_big_int = BigInteger(leading_zeroes)
+        leading_zeroes_hash = hash_elems(leading_zeroes_big_int)
+
+        # Assert.
+        self.assertEqual(input, leading_zeroes_big_int)
+        self.assertEqual(input_hash, leading_zeroes_hash)
+
+    @given(elements_mod_p())
+    def test_hash_of_big_integer_with_single_leading_zero(
+        self, input: ElementModP
+    ) -> None:
+        """Test hashing of big integer with a single leading zero creating an invalid hex byte reprsentation."""
+
+        # Arrange.
+        invalid_hex = "0" + input.to_hex()
+        input_hash = hash_elems(input)
+
+        # Act.
+        invalid_hex_big_int = BigInteger(invalid_hex)
+        invalid_hex_hash = hash_elems(invalid_hex_big_int)
+
+        # Assert.
+        self.assertEqual(input, invalid_hex_big_int)
+        self.assertEqual(input_hash, invalid_hex_hash)
+
     def test_hash_for_zero_number_is_zero_string(self):
         self.assertEqual(hash_elems(0), hash_elems("0"))
 

For ElementModQ, q1 == q2 does not imply that hash(q1) == hash(q2)
### Is there an existing issue for this?

- [X] I have searched the existing issues

### Current Behavior

For ElementModQ, q1 == q2 does not imply that hash(q1) == hash(q2)

```
    def testHexToQ(self) -> None :
        s1q = hex_to_q("C49A1E8053FBA95F6B7CD3F3B30B101CDD595C435A46AECF2872F47F1C601206")
        s2q = hex_to_q("0C49A1E8053FBA95F6B7CD3F3B30B101CDD595C435A46AECF2872F47F1C601206")
        self.assertEqual(s1q, s2q)
        self.assertNotEqual(hash_elems(s1q), hash_elems(s2q))
        self.assertNotEqual(s1q.to_hex(), s2q.to_hex())
```

its suprising that leading zeroes are retained, even past the 64 char maximum:

```
    def testHexString(self) -> None :
        self.show("A1E8053FBA95F6B7CD3F3B30B101CDD595C435A46AECF2872F47F1C601206")
        self.show("9A1E8053FBA95F6B7CD3F3B30B101CDD595C435A46AECF2872F47F1C601206")
        self.show("49A1E8053FBA95F6B7CD3F3B30B101CDD595C435A46AECF2872F47F1C601206")
        self.show("C49A1E8053FBA95F6B7CD3F3B30B101CDD595C435A46AECF2872F47F1C601206")
        self.show("0C49A1E8053FBA95F6B7CD3F3B30B101CDD595C435A46AECF2872F47F1C601206")
        self.show("00C49A1E8053FBA95F6B7CD3F3B30B101CDD595C435A46AECF2872F47F1C601206")
        self.show("000C49A1E8053FBA95F6B7CD3F3B30B101CDD595C435A46AECF2872F47F1C601206")
        self.show("0000C49A1E8053FBA95F6B7CD3F3B30B101CDD595C435A46AECF2872F47F1C601206")

    def show(self, s1 : str) -> None :
        s1q = hex_to_q(s1)
        print(f" len = {len(s1)} hex = {s1q.to_hex()}")
```

gives:

```
 len = 61 hex = A1E8053FBA95F6B7CD3F3B30B101CDD595C435A46AECF2872F47F1C601206
 len = 62 hex = 9A1E8053FBA95F6B7CD3F3B30B101CDD595C435A46AECF2872F47F1C601206
 len = 63 hex = 49A1E8053FBA95F6B7CD3F3B30B101CDD595C435A46AECF2872F47F1C601206
 len = 64 hex = C49A1E8053FBA95F6B7CD3F3B30B101CDD595C435A46AECF2872F47F1C601206
 len = 65 hex = 0C49A1E8053FBA95F6B7CD3F3B30B101CDD595C435A46AECF2872F47F1C601206
 len = 66 hex = 00C49A1E8053FBA95F6B7CD3F3B30B101CDD595C435A46AECF2872F47F1C601206
 len = 67 hex = 000C49A1E8053FBA95F6B7CD3F3B30B101CDD595C435A46AECF2872F47F1C601206
 len = 68 hex = 0000C49A1E8053FBA95F6B7CD3F3B30B101CDD595C435A46AECF2872F47F1C601206
```



### Expected Behavior

Should be:

```
    def testHexToQ(self) -> None :
        s1q = hex_to_q("C49A1E8053FBA95F6B7CD3F3B30B101CDD595C435A46AECF2872F47F1C601206")
        s2q = hex_to_q("0C49A1E8053FBA95F6B7CD3F3B30B101CDD595C435A46AECF2872F47F1C601206")
        self.assertEqual(s1q, s2q)
        self.assertEqual(hash_elems(s1q), hash_elems(s2q))
        self.assertEqual(s1q.to_hex(), s2q.to_hex())
```

would recommend that the hex value be exactly 64 characters, with leading zeros truncated or padded as needed.

### Steps To Reproduce

see above

### Environment

```markdown
- OS: all
```


### Anything else?

The same is probably true for ElementModP

In general, the hash function is underspecified.
Porting it to other languages is problematic, since there is no specification other than the python code.
There is a small enough set of argument types to hash_elems() that specifying it should not be too onerous to do.
Then one can test to look for other possible edge cases and make sure there is no ambiguity.
tests/property/test_hash.py::TestHash::test_hash_of_big_integer_with_single_leading_zero tests/property/test_hash.py::TestHash::test_hash_of_big_integer_with_leading_zero_bytes
tests/property/test_hash.py::TestHash::test_hash_value_from_nested_list_and_result_of_hashed_list_by_taking_the_hex tests/property/test_hash.py::TestHash::test_hash_for_non_zero_number_string_same_as_explicit_number tests/property/test_hash.py::TestHash::test_same_answer_twice_in_a_row tests/property/test_hash.py::TestHash::test_hash_of_save_values_in_list_are_same_hash tests/property/test_hash.py::TestHash::test_hash_for_none_same_as_null_string tests/property/test_hash.py::TestHash::test_basic_hash_properties tests/property/test_hash.py::TestHash::test_hash_for_zero_number_is_zero_string tests/property/test_hash.py::TestHash::test_hash_not_null_equivalents tests/property/test_hash.py::TestHash::test_hash_null_equivalents tests/property/test_hash.py::TestHash::test_different_strings_casing_not_the_same_hash