Skip to content

Commit

Permalink
Type annotations for nacl.bindings.crypto_aead. (#699)
Browse files Browse the repository at this point in the history
Mypy config
-----------

Due to CFFI there are lots of `Any`s in play here. So first, adjust the
mypy config to tolerate `Any`s in the bindings module. The way I've done
this is a bit awkward. My first attempt included
`nacl.bindings.crypto_aead` in the existing block of overrides, together
with an additional override for `nacl.bindings.*` which relaxed the `Any`
checks. This doesn't work, and the [mypy docs](https://mypy.readthedocs.io/en/stable/config_file.html#config-file-format)
recognise this as being unfortunate.

I then tried to target the addtional override to just
`nacl.bindings.crypto_aead`, but this failed with:

```
pyproject.toml: toml config file contains [[tool.mypy.overrides]]
sections with conflicting values. Module 'nacl.bindings.crypto_aead' has
two different values for 'disallow_any_expr'
```

Annotations
-----------

Largely copied from #692, which itself cribbed types from docstrings.
There are two changes on top of that:

- I marked the constants at the top of the module as `int`s. They're
  `size_t`s in the corresponding C header. I'm not sure this is
  important (I think they get used with CFFI only?), but it drives down
  the number of `Any`s.
- The code suggested that `aead` was allowed to be an `Optional[bytes]`
  because we allow it to be `None` everywhere. I've changed the
  docstring and annotation to reflect this. I, erm, have no idea if
  passing `None`/`NULL` to the relevant functions is cryptographically
  meaningful or safe.
  • Loading branch information
DMRobertson committed Nov 15, 2021
1 parent 54a5ee2 commit 6bf77d9
Show file tree
Hide file tree
Showing 2 changed files with 61 additions and 30 deletions.
21 changes: 19 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -25,14 +25,16 @@ module = [
]
ignore_missing_imports = true

# Most options can be applied across the entire project.
[[tool.mypy.overrides]]
module = [
"nacl.bindings.crypto_aead",
"nacl.encoding",
"nacl.exceptions",
"nacl.utils",
]
disallow_any_unimported = true
disallow_any_expr = true
# disallow_any_expr: true outside of `bindings`
disallow_any_decorated = true
disallow_any_explicit = true
disallow_any_generics = true
Expand All @@ -48,8 +50,23 @@ no_implicit_optional = true

warn_unused_ignores = true
warn_no_return = true
warn_return_any = true
# warn_return_any: true outside of `bindings`
warn_unreachable = true

# no-implicit-reexport enabled globally
strict_equality = true

# Within `nacl.bindings`, all of the C functions exposed via cffi in
# nacl._sodium return `Any` as far as mypy is concerned. It's not worth it to
# stub the C functions or cast() their uses. But this means there are more
# `Any`s floating around. So the more restrictive any checks we'd like to use
# should only be turned out outside of `bindings`.

[[tool.mypy.overrides]]
module = [
"nacl.encoding",
"nacl.exceptions",
]
disallow_any_expr = true
warn_return_any = true

70 changes: 42 additions & 28 deletions src/nacl/bindings/crypto_aead.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

from typing import Optional

from nacl import exceptions as exc
from nacl._sodium import ffi, lib
Expand All @@ -23,57 +23,59 @@
authenticator
"""

crypto_aead_chacha20poly1305_ietf_KEYBYTES = (
crypto_aead_chacha20poly1305_ietf_KEYBYTES: int = (
lib.crypto_aead_chacha20poly1305_ietf_keybytes()
)
crypto_aead_chacha20poly1305_ietf_NSECBYTES = (
crypto_aead_chacha20poly1305_ietf_NSECBYTES: int = (
lib.crypto_aead_chacha20poly1305_ietf_nsecbytes()
)
crypto_aead_chacha20poly1305_ietf_NPUBBYTES = (
crypto_aead_chacha20poly1305_ietf_NPUBBYTES: int = (
lib.crypto_aead_chacha20poly1305_ietf_npubbytes()
)
crypto_aead_chacha20poly1305_ietf_ABYTES = (
crypto_aead_chacha20poly1305_ietf_ABYTES: int = (
lib.crypto_aead_chacha20poly1305_ietf_abytes()
)
crypto_aead_chacha20poly1305_ietf_MESSAGEBYTES_MAX = (
crypto_aead_chacha20poly1305_ietf_MESSAGEBYTES_MAX: int = (
lib.crypto_aead_chacha20poly1305_ietf_messagebytes_max()
)
_aead_chacha20poly1305_ietf_CRYPTBYTES_MAX = (
crypto_aead_chacha20poly1305_ietf_MESSAGEBYTES_MAX
+ crypto_aead_chacha20poly1305_ietf_ABYTES
)

crypto_aead_chacha20poly1305_KEYBYTES = (
crypto_aead_chacha20poly1305_KEYBYTES: int = (
lib.crypto_aead_chacha20poly1305_keybytes()
)
crypto_aead_chacha20poly1305_NSECBYTES = (
crypto_aead_chacha20poly1305_NSECBYTES: int = (
lib.crypto_aead_chacha20poly1305_nsecbytes()
)
crypto_aead_chacha20poly1305_NPUBBYTES = (
crypto_aead_chacha20poly1305_NPUBBYTES: int = (
lib.crypto_aead_chacha20poly1305_npubbytes()
)
crypto_aead_chacha20poly1305_ABYTES = lib.crypto_aead_chacha20poly1305_abytes()
crypto_aead_chacha20poly1305_MESSAGEBYTES_MAX = (
crypto_aead_chacha20poly1305_ABYTES: int = (
lib.crypto_aead_chacha20poly1305_abytes()
)
crypto_aead_chacha20poly1305_MESSAGEBYTES_MAX: int = (
lib.crypto_aead_chacha20poly1305_messagebytes_max()
)
_aead_chacha20poly1305_CRYPTBYTES_MAX = (
crypto_aead_chacha20poly1305_MESSAGEBYTES_MAX
+ crypto_aead_chacha20poly1305_ABYTES
)

crypto_aead_xchacha20poly1305_ietf_KEYBYTES = (
crypto_aead_xchacha20poly1305_ietf_KEYBYTES: int = (
lib.crypto_aead_xchacha20poly1305_ietf_keybytes()
)
crypto_aead_xchacha20poly1305_ietf_NSECBYTES = (
crypto_aead_xchacha20poly1305_ietf_NSECBYTES: int = (
lib.crypto_aead_xchacha20poly1305_ietf_nsecbytes()
)
crypto_aead_xchacha20poly1305_ietf_NPUBBYTES = (
crypto_aead_xchacha20poly1305_ietf_NPUBBYTES: int = (
lib.crypto_aead_xchacha20poly1305_ietf_npubbytes()
)
crypto_aead_xchacha20poly1305_ietf_ABYTES = (
crypto_aead_xchacha20poly1305_ietf_ABYTES: int = (
lib.crypto_aead_xchacha20poly1305_ietf_abytes()
)
crypto_aead_xchacha20poly1305_ietf_MESSAGEBYTES_MAX = (
crypto_aead_xchacha20poly1305_ietf_MESSAGEBYTES_MAX: int = (
lib.crypto_aead_xchacha20poly1305_ietf_messagebytes_max()
)
_aead_xchacha20poly1305_ietf_CRYPTBYTES_MAX = (
Expand All @@ -82,15 +84,17 @@
)


def crypto_aead_chacha20poly1305_ietf_encrypt(message, aad, nonce, key):
def crypto_aead_chacha20poly1305_ietf_encrypt(
message: bytes, aad: Optional[bytes], nonce: bytes, key: bytes
) -> bytes:
"""
Encrypt the given ``message`` using the IETF ratified chacha20poly1305
construction described in RFC7539.
:param message:
:type message: bytes
:param aad:
:type aad: bytes
:type aad: Optional[bytes]
:param nonce:
:type nonce: bytes
:param key:
Expand Down Expand Up @@ -159,15 +163,17 @@ def crypto_aead_chacha20poly1305_ietf_encrypt(message, aad, nonce, key):
return ffi.buffer(ciphertext, clen[0])[:]


def crypto_aead_chacha20poly1305_ietf_decrypt(ciphertext, aad, nonce, key):
def crypto_aead_chacha20poly1305_ietf_decrypt(
ciphertext: bytes, aad: Optional[bytes], nonce: bytes, key: bytes
) -> bytes:
"""
Decrypt the given ``ciphertext`` using the IETF ratified chacha20poly1305
construction described in RFC7539.
:param ciphertext:
:type ciphertext: bytes
:param aad:
:type aad: bytes
:type aad: Optional[bytes]
:param nonce:
:type nonce: bytes
:param key:
Expand Down Expand Up @@ -236,15 +242,17 @@ def crypto_aead_chacha20poly1305_ietf_decrypt(ciphertext, aad, nonce, key):
return ffi.buffer(message, mlen[0])[:]


def crypto_aead_chacha20poly1305_encrypt(message, aad, nonce, key):
def crypto_aead_chacha20poly1305_encrypt(
message: bytes, aad: Optional[bytes], nonce: bytes, key: bytes
) -> bytes:
"""
Encrypt the given ``message`` using the "legacy" construction
described in draft-agl-tls-chacha20poly1305.
:param message:
:type message: bytes
:param aad:
:type aad: bytes
:type aad: Optional[bytes]
:param nonce:
:type nonce: bytes
:param key:
Expand Down Expand Up @@ -314,15 +322,17 @@ def crypto_aead_chacha20poly1305_encrypt(message, aad, nonce, key):
return ffi.buffer(ciphertext, clen[0])[:]


def crypto_aead_chacha20poly1305_decrypt(ciphertext, aad, nonce, key):
def crypto_aead_chacha20poly1305_decrypt(
ciphertext: bytes, aad: Optional[bytes], nonce: bytes, key: bytes
) -> bytes:
"""
Decrypt the given ``ciphertext`` using the "legacy" construction
described in draft-agl-tls-chacha20poly1305.
:param ciphertext: authenticated ciphertext
:type ciphertext: bytes
:param aad:
:type aad: bytes
:type aad: Optional[bytes]
:param nonce:
:type nonce: bytes
:param key:
Expand Down Expand Up @@ -391,15 +401,17 @@ def crypto_aead_chacha20poly1305_decrypt(ciphertext, aad, nonce, key):
return ffi.buffer(message, mlen[0])[:]


def crypto_aead_xchacha20poly1305_ietf_encrypt(message, aad, nonce, key):
def crypto_aead_xchacha20poly1305_ietf_encrypt(
message: bytes, aad: Optional[bytes], nonce: bytes, key: bytes
) -> bytes:
"""
Encrypt the given ``message`` using the long-nonces xchacha20poly1305
construction.
:param message:
:type message: bytes
:param aad:
:type aad: bytes
:type aad: Optional[bytes]
:param nonce:
:type nonce: bytes
:param key:
Expand Down Expand Up @@ -469,15 +481,17 @@ def crypto_aead_xchacha20poly1305_ietf_encrypt(message, aad, nonce, key):
return ffi.buffer(ciphertext, clen[0])[:]


def crypto_aead_xchacha20poly1305_ietf_decrypt(ciphertext, aad, nonce, key):
def crypto_aead_xchacha20poly1305_ietf_decrypt(
ciphertext: bytes, aad: Optional[bytes], nonce: bytes, key: bytes
) -> bytes:
"""
Decrypt the given ``ciphertext`` using the long-nonces xchacha20poly1305
construction.
:param ciphertext: authenticated ciphertext
:type ciphertext: bytes
:param aad:
:type aad: bytes
:type aad: Optional[bytes]
:param nonce:
:type nonce: bytes
:param key:
Expand Down

0 comments on commit 6bf77d9

Please sign in to comment.