Repository: pyca/cryptography

Issue:
"InternalError: Unknown OpenSSL error." when computing point at infinity of SECP256R1
Hi,

I'm running version 3.4.8 of pyca/cryptography in Python 3.9.5, installed viva pip. 

(Indirectly) computing the point at infinity of SECP256R1 results in an OpenSSL error that is not recognized by pyca/cryptography. Not sure if the non-recognition is intended, since the Traceback output still points to the issue at the very end.

The code to reproduce is
```python
import cryptography
print("pyca/cryptography Version:", cryptography.__version__)

from cryptography.hazmat.primitives.asymmetric import ec
q = 0xffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551 # SECP256R1's order
sk = ec.derive_private_key(q, ec.SECP256R1())
```

Running it on my machine results in 
```
pyca/cryptography Version: 3.4.8
Traceback (most recent call last):
  File "/home/fernando/bug.py", line 7, in <module>
    sk = ec.derive_private_key(q, ec.SECP256R1())
  File "/home/fernando/bin/sage-9.4/local/lib/python3.9/site-packages/cryptography/hazmat/primitives/asymmetric/ec.py", line 355, in derive_private_key
    return backend.derive_elliptic_curve_private_key(private_value, curve)
  File "/home/fernando/bin/sage-9.4/local/lib/python3.9/site-packages/cryptography/hazmat/backends/openssl/backend.py", line 1611, in derive_elliptic_curve_private_key
    self.openssl_assert(res == 1)
  File "/home/fernando/bin/sage-9.4/local/lib/python3.9/site-packages/cryptography/hazmat/backends/openssl/backend.py", line 241, in openssl_assert
    return binding._openssl_assert(self._lib, ok, errors=errors)
  File "/home/fernando/bin/sage-9.4/local/lib/python3.9/site-packages/cryptography/hazmat/bindings/openssl/binding.py", line 80, in _openssl_assert
    raise InternalError(
cryptography.exceptions.InternalError: Unknown OpenSSL error. This error is commonly encountered when another library is not cleaning up the OpenSSL error stack. If you are using cryptography with another library that uses OpenSSL try disabling it before reporting a bug. Otherwise please file an issue at https://github.com/pyca/cryptography/issues with information on how to reproduce this. ([_OpenSSLErrorWithText(code=269635690, lib=16, func=293, reason=106, reason_text=b'error:1012506A:elliptic curve routines:EC_POINT_get_affine_coordinates:point at infinity')])
```

Cheers
Fernando



Discussion:
We consider all InternalErrors to be bugs, we need to make sure the code raises a proper exception here (likely ValueError).
Tentatively attaching myself to this issue (though happy to open a new one). While trying to [build](https://github.com/conda-forge/cryptography-feedstock/pull/76) cryptography 35.x against OpenSSL 3.0 in conda-forge, I get
```
cryptography.exceptions.InternalError: Unknown OpenSSL error. This error is commonly encountered when another library is not cleaning up the OpenSSL error stack. If you are using cryptography with another library that uses OpenSSL try disabling it before reporting a bug. Otherwise please file an issue at https://github.com/pyca/cryptography/issues with information on how to reproduce this. ([_OpenSSLErrorWithText(code=310378599, lib=37, reason=103, reason_text=b'error:12800067:DSO support routines::could not load the shared library'), _OpenSSLErrorWithText(code=310378599, lib=37, reason=103, reason_text=b'error:12800067:DSO support routines::could not load the shared library'), _OpenSSLErrorWithText(code=126615813, lib=15, reason=786693, reason_text=b'error:078C0105:common libcrypto routines::init fail')])
```

upon trying `from cryptography.hazmat.backends.openssl import backend`, although this _only_ happens on windows. It's possible that this is a bug somewhere else (e.g. CPython, or conda-forge's version thereof), but since you said:
> We consider all InternalErrors to be bugs

I thought I'd bring it up.
You've found the one exception @h-vetinari! Your issue appears to be stemming from something with your build/linking process resulting in OpenSSL not being able to init libcrypto due to a DSO load failure. Since that's an error resulting from the near-infinite ways OpenSSL can be built, linked, and distributed we can't really wrap that effectively. We could choose to raise a custom exception that just says InitializationError or something, but I don't think that provides any real value over the existing situation.
@h-vetinari this bug is about an issue with elliptic curve operations, your issue is not related so this isn't the right place for it.
OK, thanks a lot for the quick responses! I wanted to avoid duplicate issues, but I'll try to fix this on conda-forge side (and will come back in a separate issue if stuck).
@h-vetinari Try configuring OpenSSL 3.0 with "enable-legacy shared no-module" switches. Shared libraries are bad things on Windows IMO. Good luck!

Resolving patch:
diff --git a/src/cryptography/hazmat/backends/openssl/backend.py b/src/cryptography/hazmat/backends/openssl/backend.py
--- a/src/cryptography/hazmat/backends/openssl/backend.py
+++ b/src/cryptography/hazmat/backends/openssl/backend.py
@@ -1266,7 +1266,9 @@ def derive_elliptic_curve_private_key(self, private_value, curve):
             bn_y = self._lib.BN_CTX_get(bn_ctx)
 
             res = get_func(group, point, bn_x, bn_y, bn_ctx)
-            self.openssl_assert(res == 1)
+            if res != 1:
+                self._consume_errors()
+                raise ValueError("Unable to derive key from private_value")
 
         res = self._lib.EC_KEY_set_public_key(ec_cdata, point)
         self.openssl_assert(res == 1)


Test patch:
diff --git a/tests/hazmat/primitives/test_ec.py b/tests/hazmat/primitives/test_ec.py
--- a/tests/hazmat/primitives/test_ec.py
+++ b/tests/hazmat/primitives/test_ec.py
@@ -129,6 +129,15 @@ def test_derive_private_key_errors(backend):
         ec.derive_private_key(-7, curve, backend)
 
 
+def test_derive_point_at_infinity(backend):
+    curve = ec.SECP256R1()
+    _skip_curve_unsupported(backend, curve)
+    # order of the curve
+    q = 0xFFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551
+    with pytest.raises(ValueError, match="Unable to derive"):
+        ec.derive_private_key(q, ec.SECP256R1())
+
+
 def test_ec_numbers():
     numbers = ec.EllipticCurvePrivateNumbers(
         1, ec.EllipticCurvePublicNumbers(2, 3, DummyCurve())