Repository: pyca/cryptography

Issue:
Documentation of DSA signature references incorrect RFC
Documentation of DSA signatures (https://cryptography.io/en/latest/hazmat/primitives/asymmetric/dsa/#signing) references RFC 6979 which sounds strange. Same for the naming of de/encoding functions at https://cryptography.io/en/latest/hazmat/primitives/asymmetric/utils/#cryptography.hazmat.primitives.asymmetric.utils.decode_rfc6979_signature

But that RFC doesn't actually define the {r,s} encoding. The actual asn1 module can be found in RFC 3279 which defines both dsa-with-sha1 signature and Dss-Sig-Value which is the {r,s} sequence.

The references to RFC 6979 are actually unfortunate, because it defines deterministic DSA signatures, while cryptography.io exposes the randomised version using openssl's `DSA_sign`.



Discussion:
Ugh, I'm not even sure why I thought 6979 defines the ASN.1 sequence for dss-sig-value. It, in fact, states that there's no standard:

```
How a signature is to be
       encoded is not covered by the DSA and ECDSA standards themselves;
       a common way is to use a DER-encoded ASN.1 structure (a SEQUENCE
       of two INTEGERs, for r and s, in that order).
```

Maybe we should alias the name to encode_sigvalue or something similar to that.


Resolving patch:
diff --git a/src/cryptography/hazmat/primitives/asymmetric/utils.py b/src/cryptography/hazmat/primitives/asymmetric/utils.py
--- a/src/cryptography/hazmat/primitives/asymmetric/utils.py
+++ b/src/cryptography/hazmat/primitives/asymmetric/utils.py
@@ -4,12 +4,16 @@
 
 from __future__ import absolute_import, division, print_function
 
+import warnings
+
 from pyasn1.codec.der import decoder, encoder
 from pyasn1.error import PyAsn1Error
 from pyasn1.type import namedtype, univ
 
 import six
 
+from cryptography import utils
+
 
 class _DSSSigValue(univ.Sequence):
     componentType = namedtype.NamedTypes(
@@ -19,6 +23,17 @@ class _DSSSigValue(univ.Sequence):
 
 
 def decode_rfc6979_signature(signature):
+    warnings.warn(
+        "decode_rfc6979_signature is deprecated and will "
+        "be removed in a future version, use decode_dss_signature instead "
+        "instead.",
+        utils.DeprecatedIn10,
+        stacklevel=2
+    )
+    return decode_dss_signature(signature)
+
+
+def decode_dss_signature(signature):
     try:
         data, remaining = decoder.decode(signature, asn1Spec=_DSSSigValue())
     except PyAsn1Error:
@@ -35,6 +50,17 @@ def decode_rfc6979_signature(signature):
 
 
 def encode_rfc6979_signature(r, s):
+    warnings.warn(
+        "encode_rfc6979_signature is deprecated and will "
+        "be removed in a future version, use encode_dss_signature instead "
+        "instead.",
+        utils.DeprecatedIn10,
+        stacklevel=2
+    )
+    return encode_dss_signature(r, s)
+
+
+def encode_dss_signature(r, s):
     if (
         not isinstance(r, six.integer_types) or
         not isinstance(s, six.integer_types)
diff --git a/src/cryptography/utils.py b/src/cryptography/utils.py
--- a/src/cryptography/utils.py
+++ b/src/cryptography/utils.py
@@ -13,6 +13,7 @@
 
 
 DeprecatedIn09 = DeprecationWarning
+DeprecatedIn10 = PendingDeprecationWarning
 
 
 def read_only_property(name):


Test patch:
diff --git a/tests/hazmat/primitives/test_asym_utils.py b/tests/hazmat/primitives/test_asym_utils.py
--- a/tests/hazmat/primitives/test_asym_utils.py
+++ b/tests/hazmat/primitives/test_asym_utils.py
@@ -7,64 +7,72 @@
 import pytest
 
 from cryptography.hazmat.primitives.asymmetric.utils import (
-    decode_rfc6979_signature, encode_rfc6979_signature
+    decode_dss_signature, decode_rfc6979_signature,
+    encode_dss_signature, encode_rfc6979_signature
 )
 
 
-def test_rfc6979_signature():
-    sig = encode_rfc6979_signature(1, 1)
+def test_deprecated_rfc6979_signature():
+    sig = pytest.deprecated_call(encode_rfc6979_signature, 1, 1)
     assert sig == b"0\x06\x02\x01\x01\x02\x01\x01"
-    assert decode_rfc6979_signature(sig) == (1, 1)
+    decoded = pytest.deprecated_call(decode_rfc6979_signature, sig)
+    assert decoded == (1, 1)
+
+
+def test_dss_signature():
+    sig = encode_dss_signature(1, 1)
+    assert sig == b"0\x06\x02\x01\x01\x02\x01\x01"
+    assert decode_dss_signature(sig) == (1, 1)
 
     r_s1 = (
         1037234182290683143945502320610861668562885151617,
         559776156650501990899426031439030258256861634312
     )
-    sig2 = encode_rfc6979_signature(*r_s1)
+    sig2 = encode_dss_signature(*r_s1)
     assert sig2 == (
         b'0-\x02\x15\x00\xb5\xaf0xg\xfb\x8bT9\x00\x13\xccg\x02\r\xdf\x1f,\x0b'
         b'\x81\x02\x14b\r;"\xabP1D\x0c>5\xea\xb6\xf4\x81)\x8f\x9e\x9f\x08'
     )
-    assert decode_rfc6979_signature(sig2) == r_s1
+    assert decode_dss_signature(sig2) == r_s1
 
-    sig3 = encode_rfc6979_signature(0, 0)
+    sig3 = encode_dss_signature(0, 0)
     assert sig3 == b"0\x06\x02\x01\x00\x02\x01\x00"
-    assert decode_rfc6979_signature(sig3) == (0, 0)
+    assert decode_dss_signature(sig3) == (0, 0)
 
-    sig4 = encode_rfc6979_signature(-1, 0)
+    sig4 = encode_dss_signature(-1, 0)
     assert sig4 == b"0\x06\x02\x01\xFF\x02\x01\x00"
-    assert decode_rfc6979_signature(sig4) == (-1, 0)
+    assert decode_dss_signature(sig4) == (-1, 0)
 
 
-def test_encode_rfc6979_non_integer():
+def test_encode_dss_non_integer():
     with pytest.raises(ValueError):
-        encode_rfc6979_signature("h", 3)
+        encode_dss_signature("h", 3)
 
     with pytest.raises(ValueError):
-        encode_rfc6979_signature("3", "2")
+        encode_dss_signature("3", "2")
 
     with pytest.raises(ValueError):
-        encode_rfc6979_signature(3, "h")
+        encode_dss_signature(3, "h")
 
     with pytest.raises(ValueError):
-        encode_rfc6979_signature(3.3, 1.2)
+        encode_dss_signature(3.3, 1.2)
 
     with pytest.raises(ValueError):
-        encode_rfc6979_signature("hello", "world")
+        encode_dss_signature("hello", "world")
 
 
-def test_decode_rfc6979_trailing_bytes():
+def test_decode_dss_trailing_bytes():
     with pytest.raises(ValueError):
-        decode_rfc6979_signature(b"0\x06\x02\x01\x01\x02\x01\x01\x00\x00\x00")
+        decode_dss_signature(b"0\x06\x02\x01\x01\x02\x01\x01\x00\x00\x00")
 
 
-def test_decode_rfc6979_invalid_asn1():
+def test_decode_dss_invalid_asn1():
     with pytest.raises(ValueError):
         # This byte sequence has an invalid ASN.1 sequence length as well as
         # an invalid integer length for the second integer.
-        decode_rfc6979_signature(b"0\x07\x02\x01\x01\x02\x02\x01")
+        decode_dss_signature(b"0\x07\x02\x01\x01\x02\x02\x01")
 
     with pytest.raises(ValueError):
         # This is the BER "end-of-contents octets," which older versions of
         # pyasn1 are wrongly willing to return from top-level DER decoding.
-        decode_rfc6979_signature(b"\x00\x00")
+        decode_dss_signature(b"\x00\x00")
diff --git a/tests/hazmat/primitives/test_dsa.py b/tests/hazmat/primitives/test_dsa.py
--- a/tests/hazmat/primitives/test_dsa.py
+++ b/tests/hazmat/primitives/test_dsa.py
@@ -17,7 +17,7 @@
 from cryptography.hazmat.primitives import hashes, serialization
 from cryptography.hazmat.primitives.asymmetric import dsa
 from cryptography.hazmat.primitives.asymmetric.utils import (
-    encode_rfc6979_signature
+    encode_dss_signature
 )
 from cryptography.utils import bit_length
 
@@ -567,7 +567,7 @@ def test_dsa_verification(self, vector, backend):
             ),
             y=vector['y']
         ).public_key(backend)
-        sig = encode_rfc6979_signature(vector['r'], vector['s'])
+        sig = encode_dss_signature(vector['r'], vector['s'])
         verifier = public_key.verifier(sig, algorithm())
         verifier.update(vector['msg'])
         if vector['result'] == "F":