From 2c749efaea40736980c4c3ff5cb5d84f2553e97a Mon Sep 17 00:00:00 2001 From: Wim Bekker Date: Fri, 13 Mar 2026 14:36:38 +0100 Subject: [PATCH 1/7] Export crypto_onetimeauth and verify to bindings --- src/bindings/crypto_onetimeauth.h | 12 ++++++++ src/nacl/bindings/__init__.py | 6 ++++ src/nacl/bindings/crypto_onetimeauth.py | 37 +++++++++++++++++++++++++ 3 files changed, 55 insertions(+) create mode 100644 src/bindings/crypto_onetimeauth.h create mode 100644 src/nacl/bindings/crypto_onetimeauth.py diff --git a/src/bindings/crypto_onetimeauth.h b/src/bindings/crypto_onetimeauth.h new file mode 100644 index 00000000..224a7e4e --- /dev/null +++ b/src/bindings/crypto_onetimeauth.h @@ -0,0 +1,12 @@ +int crypto_onetimeauth(unsigned char *out, + const unsigned char *in, + unsigned long long inlen, + const unsigned char *k); + +int crypto_onetimeauth_verify(const unsigned char *h, + const unsigned char *in, + unsigned long long inlen, + const unsigned char *k); + +size_t crypto_onetimeauth_bytes(void); +size_t crypto_onetimeauth_keybytes(void); diff --git a/src/nacl/bindings/__init__.py b/src/nacl/bindings/__init__.py index 2e07ba1e..d2bf2432 100644 --- a/src/nacl/bindings/__init__.py +++ b/src/nacl/bindings/__init__.py @@ -130,6 +130,10 @@ crypto_kx_seed_keypair, crypto_kx_server_session_keys, ) +from nacl.bindings.crypto_onetimeauth import ( + crypto_onetimeauth, + crypto_onetimeauth_verify, +) from nacl.bindings.crypto_pwhash import ( crypto_pwhash_ALG_ARGON2I13, crypto_pwhash_ALG_ARGON2ID13, @@ -378,6 +382,8 @@ "crypto_kx_SECRET_KEY_BYTES", "crypto_kx_SEED_BYTES", "crypto_kx_SESSION_KEY_BYTES", + "crypto_onetimeauth", + "crypto_onetimeauth_verify", "has_crypto_scalarmult_ed25519", "crypto_scalarmult_BYTES", "crypto_scalarmult_SCALARBYTES", diff --git a/src/nacl/bindings/crypto_onetimeauth.py b/src/nacl/bindings/crypto_onetimeauth.py new file mode 100644 index 00000000..3a3e47c2 --- /dev/null +++ b/src/nacl/bindings/crypto_onetimeauth.py @@ -0,0 +1,37 @@ +from nacl._sodium import ffi, lib + +POLY1305_BYTES = 16 # length of MAC/tag in bytes + +def crypto_onetimeauth(message: bytes, key: bytes) -> bytes: + """ + Generate Poly1305 MAC over message with key. + + :param message: data to authenticate + :param key: 32-bytes Poly1305 key + :return: 16-bytes MAC (tag) + """ + if len(key) != 32: + raise ValueError("Key must be 32 bytes") + + mac = ffi.new(f"unsigned char[{POLY1305_BYTES}]") + rc = lib.crypto_onetimeauth(mac, message, len(message), key) + if rc != 0: + raise RuntimeError(f"crypto_onetimeauth failed with code {rc}") + return ffi.buffer(mac, POLY1305_BYTES)[:] + +def crypto_onetimeauth_verify(mac: bytes, message: bytes, key: bytes) -> bool: + """ + Verify if mac is valid for message and key. + + :param mac: 16-bytes MAC/tag to check + :param message: data + :param key: 32-bytes key + :return: True on valid MAC, else False + """ + if len(mac) != POLY1305_BYTES: + raise ValueError("MAC must be 16 bytes") + if len(key) != 32: + raise ValueError("Key must be 32 bytes") + + rc = lib.crypto_onetimeauth_verify(mac, message, len(message), key) + return rc == 0 From 5a5be9857fa59f04bc5fb2a886a65e4c92220228 Mon Sep 17 00:00:00 2001 From: Wim Bekker Date: Thu, 9 Apr 2026 11:27:30 +0200 Subject: [PATCH 2/7] Reformat crypto_onetimeauth.py --- src/nacl/bindings/crypto_onetimeauth.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/nacl/bindings/crypto_onetimeauth.py b/src/nacl/bindings/crypto_onetimeauth.py index 3a3e47c2..c8c2ea09 100644 --- a/src/nacl/bindings/crypto_onetimeauth.py +++ b/src/nacl/bindings/crypto_onetimeauth.py @@ -2,6 +2,7 @@ POLY1305_BYTES = 16 # length of MAC/tag in bytes + def crypto_onetimeauth(message: bytes, key: bytes) -> bytes: """ Generate Poly1305 MAC over message with key. @@ -19,6 +20,7 @@ def crypto_onetimeauth(message: bytes, key: bytes) -> bytes: raise RuntimeError(f"crypto_onetimeauth failed with code {rc}") return ffi.buffer(mac, POLY1305_BYTES)[:] + def crypto_onetimeauth_verify(mac: bytes, message: bytes, key: bytes) -> bool: """ Verify if mac is valid for message and key. From 088e3a3035272781103a17ea7add2ca7de2930da Mon Sep 17 00:00:00 2001 From: Wim Bekker Date: Thu, 9 Apr 2026 15:06:09 +0200 Subject: [PATCH 3/7] Export and use crypto_onetimeauth BYTES and KEYBYTES --- src/nacl/bindings/__init__.py | 4 ++++ src/nacl/bindings/crypto_onetimeauth.py | 20 +++++++++++--------- 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/src/nacl/bindings/__init__.py b/src/nacl/bindings/__init__.py index d2bf2432..83d8d023 100644 --- a/src/nacl/bindings/__init__.py +++ b/src/nacl/bindings/__init__.py @@ -133,6 +133,8 @@ from nacl.bindings.crypto_onetimeauth import ( crypto_onetimeauth, crypto_onetimeauth_verify, + crypto_onetimeauth_BYTES, + crypto_onetimeauth_KEYBYTES, ) from nacl.bindings.crypto_pwhash import ( crypto_pwhash_ALG_ARGON2I13, @@ -384,6 +386,8 @@ "crypto_kx_SESSION_KEY_BYTES", "crypto_onetimeauth", "crypto_onetimeauth_verify", + "crypto_onetimeauth_BYTES", + "crypto_onetimeauth_KEYBYTES", "has_crypto_scalarmult_ed25519", "crypto_scalarmult_BYTES", "crypto_scalarmult_SCALARBYTES", diff --git a/src/nacl/bindings/crypto_onetimeauth.py b/src/nacl/bindings/crypto_onetimeauth.py index c8c2ea09..b71efcc8 100644 --- a/src/nacl/bindings/crypto_onetimeauth.py +++ b/src/nacl/bindings/crypto_onetimeauth.py @@ -1,6 +1,8 @@ from nacl._sodium import ffi, lib -POLY1305_BYTES = 16 # length of MAC/tag in bytes + +crypto_onetimeauth_BYTES = lib.crypto_onetimeauth_bytes() +crypto_onetimeauth_KEYBYTES = lib.crypto_onetimeauth_keybytes() def crypto_onetimeauth(message: bytes, key: bytes) -> bytes: @@ -11,14 +13,14 @@ def crypto_onetimeauth(message: bytes, key: bytes) -> bytes: :param key: 32-bytes Poly1305 key :return: 16-bytes MAC (tag) """ - if len(key) != 32: - raise ValueError("Key must be 32 bytes") + if len(key) != crypto_onetimeauth_KEYBYTES: + raise ValueError(f"Key must be {crypto_onetimeauth_KEYBYTES} bytes") - mac = ffi.new(f"unsigned char[{POLY1305_BYTES}]") + mac = ffi.new(f"unsigned char[{crypto_onetimeauth_BYTES}]") rc = lib.crypto_onetimeauth(mac, message, len(message), key) if rc != 0: raise RuntimeError(f"crypto_onetimeauth failed with code {rc}") - return ffi.buffer(mac, POLY1305_BYTES)[:] + return ffi.buffer(mac, crypto_onetimeauth_BYTES)[:] def crypto_onetimeauth_verify(mac: bytes, message: bytes, key: bytes) -> bool: @@ -30,10 +32,10 @@ def crypto_onetimeauth_verify(mac: bytes, message: bytes, key: bytes) -> bool: :param key: 32-bytes key :return: True on valid MAC, else False """ - if len(mac) != POLY1305_BYTES: - raise ValueError("MAC must be 16 bytes") - if len(key) != 32: - raise ValueError("Key must be 32 bytes") + if len(mac) != crypto_onetimeauth_BYTES: + raise ValueError(f"MAC must be {crypto_onetimeauth_BYTES} bytes") + if len(key) != crypto_onetimeauth_KEYBYTES: + raise ValueError(f"Key must be {crypto_onetimeauth_KEYBYTES} bytes") rc = lib.crypto_onetimeauth_verify(mac, message, len(message), key) return rc == 0 From 9720449bd67fc81f403f95d21a397d528aa72206 Mon Sep 17 00:00:00 2001 From: Wim Bekker Date: Thu, 9 Apr 2026 15:06:52 +0200 Subject: [PATCH 4/7] Add tests for onetimeauth --- tests/test_bindings.py | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/tests/test_bindings.py b/tests/test_bindings.py index a89c361c..c5864c74 100644 --- a/tests/test_bindings.py +++ b/tests/test_bindings.py @@ -1020,3 +1020,26 @@ def test_scalarmult_ed25519_unavailable(): c.crypto_scalarmult_ed25519(zero, zero) with pytest.raises(UnavailableError): c.crypto_scalarmult_ed25519_noclamp(zero, zero) + + +def test_onetimeauth_wrong_length(): + with pytest.raises(ValueError): + c.crypto_onetimeauth(b"", b"") + with pytest.raises(ValueError): + c.crypto_onetimeauth(b"message", b"") + with pytest.raises(ValueError): + c.crypto_onetimeauth_verify(b"", b"", b"") + with pytest.raises(ValueError): + c.crypto_onetimeauth_verify(b"\x00" * c.crypto_onetimeauth_BYTES, b"message", b"") + with pytest.raises(ValueError): + c.crypto_onetimeauth_verify(b"", b"message", b"\x00" * c.crypto_onetimeauth_KEYBYTES) + + +def test_onetimeauth(): + key = b"\x01" * c.crypto_onetimeauth_KEYBYTES + msg = b"message" + + mac = c.crypto_onetimeauth(msg, key) + assert tohex(mac) == "4cee137430de76761b1d11751b1d1175" + + assert c.crypto_onetimeauth_verify(mac, msg, key) From f6db61c6867c8a15aee4dc48a7a60a411b0bcb29 Mon Sep 17 00:00:00 2001 From: Wim Bekker Date: Thu, 9 Apr 2026 15:07:54 +0200 Subject: [PATCH 5/7] Remove check on lib.crypto_onetimeauth This function redirects to crypto_onetimeauth_poly1305_donna which always returns 0. --- src/nacl/bindings/crypto_onetimeauth.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/nacl/bindings/crypto_onetimeauth.py b/src/nacl/bindings/crypto_onetimeauth.py index b71efcc8..b5eec6b2 100644 --- a/src/nacl/bindings/crypto_onetimeauth.py +++ b/src/nacl/bindings/crypto_onetimeauth.py @@ -17,9 +17,7 @@ def crypto_onetimeauth(message: bytes, key: bytes) -> bytes: raise ValueError(f"Key must be {crypto_onetimeauth_KEYBYTES} bytes") mac = ffi.new(f"unsigned char[{crypto_onetimeauth_BYTES}]") - rc = lib.crypto_onetimeauth(mac, message, len(message), key) - if rc != 0: - raise RuntimeError(f"crypto_onetimeauth failed with code {rc}") + lib.crypto_onetimeauth(mac, message, len(message), key) return ffi.buffer(mac, crypto_onetimeauth_BYTES)[:] From 53a217fe01b5c230be84bbfd5cdb5e2ecfed564d Mon Sep 17 00:00:00 2001 From: Wim Bekker Date: Thu, 9 Apr 2026 15:13:49 +0200 Subject: [PATCH 6/7] Reformat test_bindings.py --- tests/test_bindings.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/tests/test_bindings.py b/tests/test_bindings.py index c5864c74..61069016 100644 --- a/tests/test_bindings.py +++ b/tests/test_bindings.py @@ -1030,9 +1030,13 @@ def test_onetimeauth_wrong_length(): with pytest.raises(ValueError): c.crypto_onetimeauth_verify(b"", b"", b"") with pytest.raises(ValueError): - c.crypto_onetimeauth_verify(b"\x00" * c.crypto_onetimeauth_BYTES, b"message", b"") + c.crypto_onetimeauth_verify( + b"\x00" * c.crypto_onetimeauth_BYTES, b"message", b"" + ) with pytest.raises(ValueError): - c.crypto_onetimeauth_verify(b"", b"message", b"\x00" * c.crypto_onetimeauth_KEYBYTES) + c.crypto_onetimeauth_verify( + b"", b"message", b"\x00" * c.crypto_onetimeauth_KEYBYTES + ) def test_onetimeauth(): From d7568d4ef0ae4d62bcf2f34dbfcd0fb47ef8bc0c Mon Sep 17 00:00:00 2001 From: Wim Bekker Date: Thu, 9 Apr 2026 15:19:46 +0200 Subject: [PATCH 7/7] Revert version number to 1.6.0 --- pyproject.toml | 2 +- src/nacl/__init__.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index eb9f2b32..6627be03 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -12,7 +12,7 @@ dynamic = ["readme"] name = "PyNaCl" # Must be kept in sync with `src/nacl/__init__.py` -version = "1.6.0.dev1" +version = "1.6.0" authors = [ {name = "The PyNaCl developers", email = "cryptography-dev@python.org"} ] diff --git a/src/nacl/__init__.py b/src/nacl/__init__.py index f251746e..ccae9959 100644 --- a/src/nacl/__init__.py +++ b/src/nacl/__init__.py @@ -22,4 +22,4 @@ __uri__ = "https://github.com/pyca/pynacl/" # Must be kept in sync with `pyproject.toml` -__version__ = "1.6.0.dev1" +__version__ = "1.6.0"